diff --git a/.gitmodules b/.gitmodules index 9dfb40cb1..456f22559 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib"] path = lib url = https://github.com/nillerusr/source-engine-libs.git +[submodule "vscript/squirrel"] + path = vscript/squirrel + url = https://github.com/albertodemichelis/squirrel diff --git a/fgdlib/gamedata.cpp b/fgdlib/gamedata.cpp index f3689ee99..b7209bd86 100644 --- a/fgdlib/gamedata.cpp +++ b/fgdlib/gamedata.cpp @@ -12,6 +12,9 @@ #include "filesystem_tools.h" #include "tier1/strtools.h" #include "utlmap.h" +#ifdef MAPBASE +#include "fmtstr.h" +#endif // MAPBASE // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -579,6 +582,33 @@ GDclass *GameData::BeginInstanceRemap( const char *pszClassName, const char *psz return m_InstanceClass; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets up for additional instance remap fixes from Mapbase +//----------------------------------------------------------------------------- +void GameData::SetupInstanceRemapParams( int iStartNodes, int iStartBrushSide, bool bRemapVecLines ) +{ + // Set the numer of nodes in the level + m_InstanceStartAINodes = iStartNodes; + + // If we have a "nodeid" key, set it to ivNodeDest so it's properly recognized + // during AI node remapping + GDinputvariable *var = m_InstanceClass->VarForName( "nodeid" ); + if ( var ) + { + var->ForceSetType( ivNodeDest ); + } + + //--------------------------------------------- + + // Set the number of brush sides in the level + m_InstanceStartSide = iStartBrushSide; + + //--------------------------------------------- + + m_bRemapVecLines = bRemapVecLines; +} +#endif // MAPBASE enum tRemapOperation { @@ -586,6 +616,13 @@ enum tRemapOperation REMAP_POSITION, REMAP_ANGLE, REMAP_ANGLE_NEGATIVE_PITCH, +#ifdef MAPBASE + // Remaps the node ID for instance/manifest AI node support + REMAP_NODE_ID, + + // Remaps brush sides and sidelists + REMAP_SIDES, +#endif // MAPBASE }; @@ -624,6 +661,12 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * RemapOperation.Insert( ivOrigin, REMAP_POSITION ); RemapOperation.Insert( ivAxis, REMAP_ANGLE ); RemapOperation.Insert( ivAngleNegativePitch, REMAP_ANGLE_NEGATIVE_PITCH ); +#ifdef MAPBASE + RemapOperation.Insert( ivNodeDest, REMAP_NODE_ID ); + RemapOperation.Insert( ivSide, REMAP_SIDES ); + RemapOperation.Insert( ivSideList, REMAP_SIDES ); + RemapOperation.Insert( ivVecLine, REMAP_POSITION ); +#endif // MAPBASE } if ( !m_InstanceClass ) @@ -657,6 +700,11 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * case REMAP_POSITION: { +#ifdef MAPBASE + // Only remap ivVecLine if the keyvalue is enabled + if (KVType == ivVecLine && !m_bRemapVecLines) + break; +#endif // MAPBASE Vector inPoint( 0.0f, 0.0f, 0.0f ), outPoint; sscanf ( pszInValue, "%f %f %f", &inPoint.x, &inPoint.y, &inPoint.z ); @@ -697,6 +745,53 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * sprintf( pszOutValue, "%g", -outAngles.x ); // just the pitch } break; +#ifdef MAPBASE + case REMAP_NODE_ID: + { + int value = atoi( pszInValue ); + if (value == -1) + break; + + //Warning( " %s %s: Remapped %i to %i", m_InstanceClass->GetName(), KVVar->GetName(), value, value + m_InstanceStartAINodes ); + + value += m_InstanceStartAINodes; + + sprintf( pszOutValue, "%i", value ); + } + break; + + case REMAP_SIDES: + { + CUtlStringList sideList; + V_SplitString( pszInValue, " ", sideList ); + + // Convert sides + CUtlStringList newSideList; + for (int i = 0; i < sideList.Count(); i++) + { + int iSide = atoi( sideList[i] ); + + //Warning( " %s %s: Remapped %i to %i", m_InstanceClass->GetName(), KVVar->GetName(), iSide, iSide + m_InstanceStartSide ); + + iSide += m_InstanceStartSide; + + newSideList.AddToTail( const_cast( CNumStr( iSide ).String() ) ); + } + + // Initial side + strcpy( pszOutValue, newSideList[0] ); + + // Start at 1 for subsequent sides + for (int i = 1; i < newSideList.Count(); i++) + { + // Any subsequent sides are spaced + sprintf( pszOutValue, "%s %s", pszOutValue, newSideList[i] ); + } + + //Warning("Old side list: \"%s\", new side list: \"%s\"\n", pszInValue, pszOutValue); + } + break; +#endif // MAPBASE } return ( strcmpi( pszInValue, pszOutValue ) != 0 ); @@ -713,7 +808,11 @@ bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char * //----------------------------------------------------------------------------- bool GameData::RemapNameField( const char *pszInValue, char *pszOutValue, TNameFixup NameFixup ) { +#ifdef MAPBASE + if ( pszInValue[ 0 ] && pszInValue[ 0 ] != '@' && pszInValue[ 0 ] != '!' ) +#else strcpy( pszOutValue, pszInValue ); +#endif // MAPBASE if ( pszInValue[ 0 ] && pszInValue[ 0 ] != '@' ) { // ! at the start of a value means it is global and should not be remaped diff --git a/game/client/C_Env_Projected_Texture.h b/game/client/C_Env_Projected_Texture.h index b15ea6ef7..094a423ce 100644 --- a/game/client/C_Env_Projected_Texture.h +++ b/game/client/C_Env_Projected_Texture.h @@ -14,6 +14,104 @@ #include "c_baseentity.h" #include "basetypes.h" +#ifdef ASW_PROJECTED_TEXTURES +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class C_EnvProjectedTexture : public C_BaseEntity +{ + DECLARE_CLASS( C_EnvProjectedTexture, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + void SetMaterial( IMaterial *pMaterial ); + void SetLightColor( byte r, byte g, byte b, byte a ); + void SetSize( float flSize ); + void SetRotation( float flRotation ); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + void ShutDownLightHandle( void ); + +#ifdef MAPBASE + virtual void Simulate(); +#else + virtual bool Simulate(); +#endif // MAPBASE + + void UpdateLight( void ); + + C_EnvProjectedTexture(); + ~C_EnvProjectedTexture(); + + static void SetVisibleBBoxMinHeight( float flVisibleBBoxMinHeight ) { m_flVisibleBBoxMinHeight = flVisibleBBoxMinHeight; } + static float GetVisibleBBoxMinHeight( void ) { return m_flVisibleBBoxMinHeight; } + static C_EnvProjectedTexture *Create( ); + +private: + + inline bool IsBBoxVisible( void ); + bool IsBBoxVisible( Vector vecExtentsMin, + Vector vecExtentsMax ); + + ClientShadowHandle_t m_LightHandle; + bool m_bForceUpdate; + + EHANDLE m_hTargetEntity; +#ifdef MAPBASE + bool m_bDontFollowTarget; +#endif // MAPBASE + + bool m_bState; + bool m_bAlwaysUpdate; + float m_flLightFOV; +#ifdef MAPBASE + float m_flLightHorFOV; +#endif // MAPBASE + bool m_bEnableShadows; + bool m_bLightOnlyTarget; + bool m_bLightWorld; + bool m_bCameraSpace; + float m_flBrightnessScale; + color32 m_LightColor; + Vector m_CurrentLinearFloatLightColor; + float m_flCurrentLinearFloatLightAlpha; +#ifdef MAPBASE + float m_flCurrentBrightnessScale; +#endif // MAPBASE + float m_flColorTransitionTime; + float m_flAmbient; + float m_flNearZ; + float m_flFarZ; + char m_SpotlightTextureName[ MAX_PATH ]; + CTextureReference m_SpotlightTexture; + int m_nSpotlightTextureFrame; + int m_nShadowQuality; +#ifdef MAPBASE + float m_flConstantAtten; + float m_flLinearAtten; + float m_flQuadraticAtten; + float m_flShadowAtten; + float m_flShadowFilter; + + bool m_bAlwaysDraw; + //bool m_bProjectedTextureVersion; +#endif // MAPBASE + + Vector m_vecExtentsMin; + Vector m_vecExtentsMax; + + static float m_flVisibleBBoxMinHeight; +}; + + + +bool C_EnvProjectedTexture::IsBBoxVisible( void ) +{ + return IsBBoxVisible( GetAbsOrigin() + m_vecExtentsMin, GetAbsOrigin() + m_vecExtentsMax ); +} + +#else + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -62,4 +160,6 @@ class C_EnvProjectedTexture : public C_BaseEntity C_EnvProjectedTexture* GetEnvProjectedTextureList(); +#endif // ASW_PROJECTED_TEXTURES + #endif // C_ENVPROJECTEDTEXTURE_H diff --git a/game/client/c_baseanimating.cpp b/game/client/c_baseanimating.cpp index d66339e53..fb9af2a47 100644 --- a/game/client/c_baseanimating.cpp +++ b/game/client/c_baseanimating.cpp @@ -54,6 +54,9 @@ #include "replay/replay_ragdoll.h" #include "studio_stats.h" #include "tier1/callqueue.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif #ifdef TF_CLIENT_DLL #include "c_tf_player.h" @@ -256,6 +259,9 @@ LINK_ENTITY_TO_CLASS( client_ragdoll, C_ClientRagdoll ); BEGIN_DATADESC( C_ClientRagdoll ) DEFINE_FIELD( m_bFadeOut, FIELD_BOOLEAN ), DEFINE_FIELD( m_bImportant, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_FIELD( m_flForcedRetireTime, FIELD_FLOAT ), +#endif // MAPBASE DEFINE_FIELD( m_iCurrentFriction, FIELD_INTEGER ), DEFINE_FIELD( m_iMinFriction, FIELD_INTEGER ), DEFINE_FIELD( m_iMaxFriction, FIELD_INTEGER ), @@ -278,6 +284,94 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( C_ClientRagdoll, C_BaseAnimating, "Client-side ragdolls" ) + + DEFINE_SCRIPTFUNC_NAMED( SUB_Remove, "FadeOut", "Fades out the ragdoll and removes it from the client." ) + + // TODO: Proper shared ragdoll funcs? + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObject, "GetRagdollObject", "Gets the ragdoll object of the specified index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObjectCount, "GetRagdollObjectCount", "Gets the number of ragdoll objects on this ragdoll." ) + +END_SCRIPTDESC(); + +ScriptHook_t C_BaseAnimating::g_Hook_OnClientRagdoll; +ScriptHook_t C_BaseAnimating::g_Hook_FireEvent; +//ScriptHook_t C_BaseAnimating::g_Hook_BuildTransformations; +#endif // MAPBASE_VSCRIPT + +BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPoseParameter, "GetPoseParameter", "Get the specified pose parameter's value" ) +#endif // MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptSetPoseParameter, "SetPoseParameter", "Set the specified pose parameter to the specified value" ) + DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptLookupAttachment, "LookupAttachment", "Get the named attachement id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentOrigin, "GetAttachmentOrigin", "Get the attachement id's origin vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentAngles, "GetAttachmentAngles", "Get the attachement id's angles as a p,y,r vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentMatrix, "GetAttachmentMatrix", "Get the attachement id's matrix transform" ) + + DEFINE_SCRIPTFUNC( LookupBone, "Get the named bone id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneTransform, "GetBoneTransform", "Get the transform for the specified bone" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetBoneTransform, "SetBoneTransform", "Set the transform for the specified bone" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAttachEntityToBone, "AttachEntityToBone", "Attaches this entity to the specified target and bone. Also allows for optional local position offset" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveBoneAttachment, "RemoveBoneAttachment", "Removes the specified bone attachment" ) + //DEFINE_SCRIPTFUNC( RemoveBoneAttachments, "Removes all bone attachments" ) + DEFINE_SCRIPTFUNC( DestroyBoneAttachments, "Destroys all bone attachments" ) + DEFINE_SCRIPTFUNC( GetNumBoneAttachments, "Gets the number of bone attachments" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneAttachment, "GetBoneAttachment", "Gets the specified bone attachment" ) + + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") + DEFINE_SCRIPTFUNC( GetBodygroup, "Gets a bodygroup" ) + DEFINE_SCRIPTFUNC( GetBodygroupName, "Gets a bodygroup name" ) + DEFINE_SCRIPTFUNC( FindBodygroupByName, "Finds a bodygroup by name" ) + DEFINE_SCRIPTFUNC( GetBodygroupCount, "Gets the number of models in a bodygroup" ) + DEFINE_SCRIPTFUNC( GetNumBodyGroups, "Gets the number of bodygroups" ) + + DEFINE_SCRIPTFUNC( GetSequence, "Gets the current sequence" ) + DEFINE_SCRIPTFUNC( SetSequence, "Sets the current sequence" ) + DEFINE_SCRIPTFUNC( SequenceLoops, "Does the current sequence loop?" ) + DEFINE_SCRIPTFUNC( LookupSequence, "Gets the index of the specified sequence name" ) + DEFINE_SCRIPTFUNC( LookupActivity, "Gets the ID of the specified activity name" ) + DEFINE_SCRIPTFUNC( GetSequenceName, "Gets the name of the specified sequence index" ) + DEFINE_SCRIPTFUNC( GetSequenceActivityName, "Gets the activity name of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceMoveDist, "GetSequenceMoveDist", "Gets the move distance of the specified sequence" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceActivity, "GetSequenceActivity", "Gets the activity ID of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSelectWeightedSequence, "SelectWeightedSequence", "Selects a sequence for the specified activity ID" ) + + DEFINE_SCRIPTFUNC( GetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( SetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( GetCycle, "" ) + DEFINE_SCRIPTFUNC( SetCycle, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSkin, "GetSkin", "Gets the model's skin" ) + DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) + + DEFINE_SCRIPTFUNC( GetForceBone, "Gets the entity's force bone, which is used to determine which bone a ragdoll should apply its force to." ) + DEFINE_SCRIPTFUNC( SetForceBone, "Sets the entity's force bone, which is used to determine which bone a ragdoll should apply its force to." ) + DEFINE_SCRIPTFUNC( GetRagdollForce, "Gets the entity's ragdoll force, which is used to apply velocity to a ragdoll." ) + DEFINE_SCRIPTFUNC( SetRagdollForce, "Sets the entity's ragdoll force, which is used to apply velocity to a ragdoll." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptBecomeRagdollOnClient, "BecomeRagdollOnClient", "" ) + DEFINE_SCRIPTFUNC( IsRagdoll, "" ) + + 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() + + //BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_BuildTransformations, "BuildTransformations", FIELD_VOID, "Called when building bone transformations. Allows VScript to read/write any bone with Get/SetBoneTransform." ) + //END_SCRIPTHOOK() +#endif // MAPBASE_VSCRIPT +END_SCRIPTDESC(); + C_ClientRagdoll::C_ClientRagdoll( bool bRestoring ) { m_iCurrentFriction = 0; @@ -287,6 +381,9 @@ C_ClientRagdoll::C_ClientRagdoll( bool bRestoring ) m_bFadingOut = false; m_bImportant = false; m_bNoModelParticles = false; +#ifdef MAPBASE + m_flForcedRetireTime = 0.0f; +#endif SetClassname("client_ragdoll"); @@ -366,7 +463,11 @@ void C_ClientRagdoll::OnRestore( void ) if ( m_bFadeOut == true ) { +#ifdef MAPBASE + s_RagdollLRU.MoveToTopOfLRU( this, m_bImportant, m_flForcedRetireTime ); +#else s_RagdollLRU.MoveToTopOfLRU( this, m_bImportant ); +#endif // MAPBASE } NoteRagdollCreationTick( this ); @@ -638,6 +739,24 @@ void C_ClientRagdoll::Release( void ) BaseClass::Release(); } +#ifdef MAPBASE_VSCRIPT +HSCRIPT C_ClientRagdoll::ScriptGetRagdollObject( int iIndex ) +{ + if (iIndex < 0 || iIndex > m_pRagdoll->RagdollBoneCount()) + { + Warning("%s GetRagdollObject: Index %i not valid (%i objects)\n", GetDebugName(), iIndex, m_pRagdoll->RagdollBoneCount()); + return NULL; + } + + return g_pScriptVM->RegisterInstance( m_pRagdoll->GetElement(iIndex) ); +} + +int C_ClientRagdoll::ScriptGetRagdollObjectCount() +{ + return m_pRagdoll->RagdollBoneCount(); +} +#endif // MAPBASE_VSCRIPT + //----------------------------------------------------------------------------- // Incremented each frame in InvalidateModelBones. Models compare this value to what it // was last time they setup their bones to determine if they need to re-setup their bones. @@ -675,6 +794,10 @@ C_BaseAnimating::C_BaseAnimating() : m_nPrevSequence = -1; m_nRestoreSequence = -1; m_pRagdoll = NULL; + m_pClientsideRagdoll = NULL; +#ifdef MAPBASE + m_pServerRagdoll = NULL; +#endif m_builtRagdoll = false; m_hitboxBoneCacheHandle = 0; m_nHitboxSet = 0; @@ -1404,6 +1527,115 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping ) return flCycle; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: Returns the world location and world angles of an attachment to vscript caller +// Input : attachment name +// Output : location and angles +//----------------------------------------------------------------------------- +const Vector& C_BaseAnimating::ScriptGetAttachmentOrigin( int iAttachment ) +{ + + static Vector absOrigin; + static QAngle qa; + + C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + + return absOrigin; +} + +const Vector& C_BaseAnimating::ScriptGetAttachmentAngles( int iAttachment ) +{ + + static Vector absOrigin; + static Vector absAngles; + static QAngle qa; + + C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + absAngles.x = qa.x; + absAngles.y = qa.y; + absAngles.z = qa.z; + return absAngles; +} + +HSCRIPT C_BaseAnimating::ScriptGetAttachmentMatrix( int iAttachment ) +{ + static matrix3x4_t matrix; + + C_BaseAnimating::GetAttachment( iAttachment, matrix ); + return g_pScriptVM->RegisterInstance( &matrix ); +} + +void C_BaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ) +{ + matrix3x4_t *matTransform = HScriptToClass( hTransform ); + if (matTransform == NULL) + return; + + GetBoneTransform( iBone, *matTransform ); +} + +void C_BaseAnimating::ScriptSetBoneTransform( int iBone, HSCRIPT hTransform ) +{ + matrix3x4_t *matTransform = HScriptToClass( hTransform ); + if (matTransform == NULL) + return; + + MatrixCopy( *matTransform, GetBoneForWrite( iBone ) ); +} + +void C_BaseAnimating::ScriptAttachEntityToBone( HSCRIPT attachTarget, int boneIndexAttached, const Vector &bonePosition, const QAngle &boneAngles ) +{ + C_BaseEntity *pTarget = ToEnt( attachTarget ); + if (pTarget == NULL) + return; + + AttachEntityToBone( pTarget->GetBaseAnimating(), boneIndexAttached, bonePosition, boneAngles ); +} + +void C_BaseAnimating::ScriptRemoveBoneAttachment( HSCRIPT boneAttachment ) +{ + C_BaseEntity *pTarget = ToEnt( boneAttachment ); + if (pTarget == NULL) + return; + + RemoveBoneAttachment( pTarget->GetBaseAnimating() ); +} + +HSCRIPT C_BaseAnimating::ScriptGetBoneAttachment( int i ) +{ + return ToHScript( GetBoneAttachment( i ) ); +} + +HSCRIPT C_BaseAnimating::ScriptBecomeRagdollOnClient() +{ + C_BaseAnimating *pRagdoll = BecomeRagdollOnClient(); + if (!pRagdoll) + return NULL; + + return pRagdoll->GetScriptInstance(); +} + +float C_BaseAnimating::ScriptGetPoseParameter( const char* szName ) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return 0.0f; + + int iPoseParam = LookupPoseParameter( pHdr, szName ); + return GetPoseParameter( iPoseParam ); +} +#endif // MAPBASE_VSCRIPT + +void C_BaseAnimating::ScriptSetPoseParameter(const char* szName, float fValue) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return; + + int iPoseParam = LookupPoseParameter(pHdr, szName); + SetPoseParameter(pHdr, iPoseParam, fValue); +} void C_BaseAnimating::GetCachedBoneMatrix( int boneIndex, matrix3x4_t &out ) { @@ -1550,7 +1782,23 @@ void C_BaseAnimating::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quater } } - +#ifdef MAPBASE_VSCRIPT + //if (m_ScriptScope.IsInitialized() && g_Hook_BuildTransformations.CanRunInScope(m_ScriptScope)) + //{ + // int oldWritableBones = m_BoneAccessor.GetWritableBones(); + // int oldReadableBones = m_BoneAccessor.GetReadableBones(); + // m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING ); + // m_BoneAccessor.SetReadableBones( BONE_USED_BY_ANYTHING ); + // + // // No parameters + // //ScriptVariant_t args[] = {}; + // //ScriptVariant_t returnValue; + // g_Hook_BuildTransformations.Call( m_ScriptScope, NULL, NULL /*&returnValue, args*/ ); + // + // m_BoneAccessor.SetWritableBones( oldWritableBones ); + // m_BoneAccessor.SetReadableBones( oldReadableBones ); + //} +#endif // MAPBASE_VSCRIPT } //----------------------------------------------------------------------------- @@ -1756,6 +2004,10 @@ CollideType_t C_BaseAnimating::GetCollideType( void ) return BaseClass::GetCollideType(); } +#ifdef MAPBASE +ConVar ai_death_pose_enabled( "ai_death_pose_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, which cancels sequence transitions while a NPC is ragdolling." ); +#endif + //----------------------------------------------------------------------------- // Purpose: if the active sequence changes, keep track of the previous ones and decay them based on their decay rate //----------------------------------------------------------------------------- @@ -1772,6 +2024,14 @@ void C_BaseAnimating::MaintainSequenceTransitions( IBoneSetup &boneSetup, float return; } +#ifdef MAPBASE + if ( IsAboutToRagdoll() && ai_death_pose_enabled.GetBool() ) + { + m_nPrevNewSequenceParity = m_nNewSequenceParity; + return; + } +#endif + m_SequenceTransitioner.CheckForSequenceChange( boneSetup.GetStudioHdr(), GetSequence(), @@ -2551,14 +2811,29 @@ void C_BaseAnimating::CalculateIKLocks( float currentTime ) // debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 ); - float d = (pTarget->est.pos - origin).Length(); + Vector vecDelta = (origin - pTarget->est.pos); + float d = vecDelta.Length(); if ( d >= flDist) continue; flDist = d; - pTarget->SetPos( origin ); - pTarget->SetAngles( angles ); +#ifdef MAPBASE + // For blending purposes, IK attachments should obey weight + if ( pTarget->est.flWeight < 1.0f ) + { + Quaternion qTarget; + AngleQuaternion( angles, qTarget ); + + QuaternionSlerp( pTarget->est.q, qTarget, pTarget->est.flWeight, pTarget->est.q ); + pTarget->SetPos( pTarget->est.pos + (vecDelta * pTarget->est.flWeight) ); + } + else +#endif // MAPBASE + { + pTarget->SetPos( origin ); + pTarget->SetAngles( angles ); + } // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 ); } @@ -3065,6 +3340,17 @@ int C_BaseAnimating::DrawModel( int flags ) int drawn = 0; +#ifdef MAPBASE + if (m_iViewHideFlags > 0) + { + // Hide this entity if it's not supposed to be drawn in this view. + if (m_iViewHideFlags & (1 << CurrentViewID())) + { + return 0; + } + } +#endif // MAPBASE + #ifdef TF_CLIENT_DLL ValidateModelIndex(); #endif @@ -3479,6 +3765,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 // MAPBASE_VSCRIPT FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); @@ -3512,6 +3803,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 // MAPBASE_VSCRIPT + FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); } } @@ -3519,6 +3815,26 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) m_flPrevEventCycle = flEventCycle; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: Implement VScript hooks +//----------------------------------------------------------------------------- +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 // MAPBASE_VSCRIPT + //----------------------------------------------------------------------------- // Purpose: Parses a muzzle effect event and sends it out for drawing // Input : *options - event parameters in text format @@ -3532,7 +3848,7 @@ bool C_BaseAnimating::DispatchMuzzleEffect( const char *options, bool isFirstPer int weaponType = 0; // Get the first parameter - p = nexttoken( token, p, ' ' ); + p = nexttoken( token, p, ' ' , sizeof(token) ); // Find the weapon type if ( token ) @@ -3576,7 +3892,7 @@ bool C_BaseAnimating::DispatchMuzzleEffect( const char *options, bool isFirstPer } // Get the second parameter - p = nexttoken( token, p, ' ' ); + p = nexttoken( token, p, ' ' , sizeof(token) ); int attachmentIndex = -1; @@ -3687,7 +4003,7 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int // Get the particle effect name const char *p = options; - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if ( token ) { const char* mtoken = ModifyEventParticles( token ); @@ -3697,7 +4013,7 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int } // Get the attachment type - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if ( token ) { iAttachType = GetAttachTypeFromString( token ); @@ -3709,7 +4025,7 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int } // Get the attachment point index - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if ( token ) { iAttachment = atoi(token); @@ -3829,18 +4145,33 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int // Eject brass case CL_EVENT_EJECTBRASS1: - if ( m_Attachments.Count() > 0 ) { - if ( MainViewOrigin().DistToSqr( GetAbsOrigin() ) < (256 * 256) ) + // Check if we're a weapon, if we belong to the local player, and if the local player is in third person - if all are true, don't do a muzzleflash in this instance, because + // we're using the view models dispatch for smoothness. + if ( dynamic_cast< C_BaseCombatWeapon *>(this) != NULL ) { - Vector attachOrigin; - QAngle attachAngles; - - if( GetAttachment( 2, attachOrigin, attachAngles ) ) + C_BaseCombatWeapon *pWeapon = dynamic_cast< C_BaseCombatWeapon *>(this); + if ( pWeapon && pWeapon->GetOwner() == C_BasePlayer::GetLocalPlayer() && ::input->CAM_IsThirdPerson() ) + break; + } + + if ( ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) ) + break; + + if ( m_Attachments.Count() > 0 ) + { + if ( MainViewOrigin().DistToSqr( GetAbsOrigin() ) < (256 * 256) ) { - tempents->EjectBrass( attachOrigin, attachAngles, GetAbsAngles(), atoi( options ) ); + Vector attachOrigin; + QAngle attachAngles; + + if( GetAttachment( 2, attachOrigin, attachAngles ) ) + { + tempents->EjectBrass( attachOrigin, attachAngles, GetAbsAngles(), atoi( options ) ); + } } } + break; } break; @@ -3854,6 +4185,36 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int case AE_NPC_MUZZLEFLASH: { // Send out the effect for an NPC +#if defined ( HL2MP ) || defined ( SDK_DLL ) // works for the modified CSS weapons included in the new template sdk. + // HL2MP - Make third person muzzleflashes as reliable as the first person ones + // while in third person the view model dispatches the muzzleflash event - note: the weapon models dispatch them too, but not frequently. + if ( IsViewModel() ) + { + C_BasePlayer *pPlayer = ToBasePlayer( dynamic_cast(this)->GetOwner() ); + if ( pPlayer && pPlayer == C_BasePlayer::GetLocalPlayer()) + { + if ( ::input->CAM_IsThirdPerson() ) + { + // Dispatch on the weapon - the player model doesn't have the attachments in hl2mp. + C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); + if ( !pWeapon ) + break; + pWeapon->DispatchMuzzleEffect( options, false ); + break; + } + } + } + + // Check if we're a weapon, if we belong to the local player, and if the local player is in third person - if all are true, don't do a muzzleflash in this instance, because + // we're using the view models dispatch for smoothness. + if ( dynamic_cast< C_BaseCombatWeapon *>(this) != NULL ) + { + C_BaseCombatWeapon *pWeapon = dynamic_cast< C_BaseCombatWeapon *>(this); + if ( pWeapon && pWeapon->GetOwner() == C_BasePlayer::GetLocalPlayer() && ::input->CAM_IsThirdPerson() ) + break; + } +#endif // HL2MP || SDK_DLL + DispatchMuzzleEffect( options, false ); break; } @@ -3914,14 +4275,14 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int const char *p = options; // Bodygroup Name - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if ( token ) { Q_strncpy( szBodygroupName, token, sizeof(szBodygroupName) ); } // Get the desired value - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if ( token ) { value = atoi( token ); @@ -3961,21 +4322,21 @@ void C_BaseAnimating::FireObsoleteEvent( const Vector& origin, const QAngle& ang const char *p = options; - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if( token ) { Q_strncpy( effectFunc, token, sizeof(effectFunc) ); } - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if( token ) { iAttachment = atoi(token); } - p = nexttoken(token, p, ' '); + p = nexttoken(token, p, ' ', sizeof(token)); if( token ) { @@ -4554,12 +4915,18 @@ void C_BaseAnimating::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matri } } +C_ClientRagdoll *C_BaseAnimating::CreateClientRagdoll( bool bRestoring ) +{ + //DevMsg( "Creating ragdoll at tick %d\n", gpGlobals->tickcount ); + return new C_ClientRagdoll( bRestoring ); +} + C_BaseAnimating *C_BaseAnimating::CreateRagdollCopy() { //Adrian: We now create a separate entity that becomes this entity's ragdoll. //That way the server side version of this entity can go away. //Plus we can hook save/restore code to these ragdolls so they don't fall on restore anymore. - C_ClientRagdoll *pRagdoll = new C_ClientRagdoll( false ); + C_ClientRagdoll *pRagdoll = CreateClientRagdoll( false ); if ( pRagdoll == NULL ) return NULL; @@ -4610,6 +4977,14 @@ C_BaseAnimating *C_BaseAnimating::CreateRagdollCopy() pRagdoll->m_nForceBone = m_nForceBone; pRagdoll->SetNextClientThink( CLIENT_THINK_ALWAYS ); +#ifdef MAPBASE + pRagdoll->m_iViewHideFlags = m_iViewHideFlags; + + pRagdoll->m_fadeMinDist = m_fadeMinDist; + pRagdoll->m_fadeMaxDist = m_fadeMaxDist; + pRagdoll->m_flFadeScale = m_flFadeScale; +#endif // MAPBASE + pRagdoll->SetModelName( AllocPooledString(pModelName) ); pRagdoll->SetModelScale( GetModelScale() ); return pRagdoll; @@ -4619,15 +4994,26 @@ C_BaseAnimating *C_BaseAnimating::BecomeRagdollOnClient() { MoveToLastReceivedPosition( true ); GetAbsOrigin(); - C_BaseAnimating *pRagdoll = CreateRagdollCopy(); + m_pClientsideRagdoll = CreateRagdollCopy(); matrix3x4_t boneDelta0[MAXSTUDIOBONES]; matrix3x4_t boneDelta1[MAXSTUDIOBONES]; matrix3x4_t currentBones[MAXSTUDIOBONES]; const float boneDt = 0.1f; GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt ); - pRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); - return pRagdoll; + m_pClientsideRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); + +#ifdef MAPBASE_VSCRIPT + // Hook for ragdolling + if (m_ScriptScope.IsInitialized() && g_Hook_OnClientRagdoll.CanRunInScope( m_ScriptScope )) + { + // ragdoll + ScriptVariant_t args[] = { ScriptVariant_t( m_pClientsideRagdoll->GetScriptInstance() ) }; + g_Hook_OnClientRagdoll.Call( m_ScriptScope, NULL, args ); + } +#endif + + return m_pClientsideRagdoll; } bool C_BaseAnimating::InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints ) @@ -5093,6 +5479,12 @@ void C_BaseAnimating::StudioFrameAdvance() if ( flNewCycle < 0.0f || flNewCycle >= 1.0f ) { + + if (flNewCycle >= 1.0f) + { + ReachedEndOfSequence(); + } + if ( IsSequenceLooping( hdr, GetSequence() ) ) { flNewCycle -= (int)(flNewCycle); diff --git a/game/client/c_baseanimating.h b/game/client/c_baseanimating.h index f91745ef1..9e53086f7 100644 --- a/game/client/c_baseanimating.h +++ b/game/client/c_baseanimating.h @@ -38,6 +38,7 @@ class C_BaseClientShader */ class IRagdoll; +class C_ClientRagdoll; class CIKContext; class CIKState; class ConVar; @@ -79,7 +80,7 @@ class CAttachmentData QAngle m_angRotation; Vector m_vOriginVelocity; int m_nLastFramecount : 31; - int m_bAnglesComputed : 1; + bool m_bAnglesComputed : 1; }; @@ -95,6 +96,7 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); DECLARE_INTERPOLATION(); + DECLARE_ENT_SCRIPTDESC(); enum { @@ -163,6 +165,17 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback 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 + // allows the multiplayer world weapon models to declare the muzzleflashes, and other effects like sp + // without the need to script it and add extra parsing code. +#endif // MAPBASE + // Parses and distributes muzzle flash events virtual bool DispatchMuzzleEffect( const char *options, bool isFirstPerson ); @@ -290,6 +303,7 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback bool IsAboutToRagdoll() const; virtual C_BaseAnimating *BecomeRagdollOnClient(); C_BaseAnimating *CreateRagdollCopy(); + virtual C_ClientRagdoll *CreateClientRagdoll( bool bRestoring = false ); bool InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints=false ); void IgniteRagdoll( C_BaseAnimating *pSource ); void TransferDissolveFrom( C_BaseAnimating *pSource ); @@ -342,6 +356,8 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback void ClientSideAnimationChanged(); virtual unsigned int ComputeClientSideAnimationFlags(); + virtual void ReachedEndOfSequence() { return; } + virtual void ResetClientsideFrame( void ) { SetCycle( 0 ); } void SetCycle( float flCycle ); @@ -445,6 +461,42 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback virtual bool IsViewModel() const; +#ifdef MAPBASE_VSCRIPT + int ScriptLookupAttachment( const char *pAttachmentName ) { return LookupAttachment( pAttachmentName ); } + const Vector& ScriptGetAttachmentOrigin(int iAttachment); + const Vector& ScriptGetAttachmentAngles(int iAttachment); + HSCRIPT ScriptGetAttachmentMatrix(int iAttachment); + + void ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ); + void ScriptSetBoneTransform( int iBone, HSCRIPT hTransform ); + + void ScriptAttachEntityToBone( HSCRIPT attachTarget, int boneIndexAttached, const Vector &bonePosition, const QAngle &boneAngles ); + void ScriptRemoveBoneAttachment( HSCRIPT boneAttachment ); + HSCRIPT ScriptGetBoneAttachment( int i ); + + int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); } + float ScriptGetSequenceMoveDist( int iSequence ) { return GetSequenceMoveDist( GetModelPtr(), iSequence ); } + int ScriptSelectWeightedSequence( int activity ) { return SelectWeightedSequence( (Activity)activity ); } + + // For VScript + int ScriptGetSkin() { return GetSkin(); } + void SetSkin( int iSkin ) { m_nSkin = iSkin; } + + int GetForceBone() { return m_nForceBone; } + void SetForceBone( int iBone ) { m_nForceBone = iBone; } + const Vector& GetRagdollForce() { return m_vecForce; } + void SetRagdollForce( const Vector &vecForce ) { m_vecForce = vecForce; } + + HSCRIPT ScriptBecomeRagdollOnClient(); + + static ScriptHook_t g_Hook_OnClientRagdoll; + static ScriptHook_t g_Hook_FireEvent; + //static ScriptHook_t g_Hook_BuildTransformations; // UNDONE: Thread access issues + + float ScriptGetPoseParameter(const char* szName); +#endif + void ScriptSetPoseParameter(const char* szName, float fValue); + protected: // View models scale their attachment positions to account for FOV. To get the unmodified // attachment position (like if you're rendering something else during the view model's DrawModel call), @@ -480,6 +532,10 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback public: CRagdoll *m_pRagdoll; + C_BaseAnimating *m_pClientsideRagdoll; // From Alien Swarm SDK +#ifdef MAPBASE + C_BaseAnimating *m_pServerRagdoll; // Not from Alien Swarm SDK (note that this can exist without the entity having died) +#endif // Texture group to use int m_nSkin; @@ -651,6 +707,9 @@ class C_ClientRagdoll : public C_BaseAnimating, public IPVSNotify C_ClientRagdoll( bool bRestoring = true ); DECLARE_CLASS( C_ClientRagdoll, C_BaseAnimating ); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif // inherited from IPVSNotify virtual void OnPVSStatusChanged( bool bInPVS ); @@ -672,8 +731,17 @@ class C_ClientRagdoll : public C_BaseAnimating, public IPVSNotify void FadeOut( void ); virtual float LastBoneChangedTime(); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetRagdollObject( int iIndex ); + int ScriptGetRagdollObjectCount(); +#endif + bool m_bFadeOut; bool m_bImportant; +#ifdef MAPBASE + // Required to save/restore Alien Swarm SDK ragdoll LRU forced fade + float m_flForcedRetireTime; +#endif float m_flEffectTime; private: diff --git a/game/client/c_basecombatcharacter.cpp b/game/client/c_basecombatcharacter.cpp index fee631181..860e68032 100644 --- a/game/client/c_basecombatcharacter.cpp +++ b/game/client/c_basecombatcharacter.cpp @@ -178,3 +178,39 @@ BEGIN_PREDICTION_DATA( C_BaseCombatCharacter ) DEFINE_PRED_ARRAY( m_hMyWeapons, FIELD_EHANDLE, MAX_WEAPONS, FTYPEDESC_INSENDTABLE ), END_PREDICTION_DATA() + +#ifdef MAPBASE_VSCRIPT + +BEGIN_ENT_SCRIPTDESC( C_BaseCombatCharacter, CBaseEntity, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAmmoCount, "GetAmmoCount", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActiveWeapon, "GetActiveWeapon", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "" ) +END_SCRIPTDESC(); + + +int C_BaseCombatCharacter::ScriptGetAmmoCount( int i ) +{ + Assert( i == -1 || i < MAX_AMMO_SLOTS ); + + if ( i < 0 || i >= MAX_AMMO_SLOTS ) + return NULL; + + return GetAmmoCount( i ); +} + +HSCRIPT C_BaseCombatCharacter::ScriptGetActiveWeapon() +{ + return ToHScript( GetActiveWeapon() ); +} + +HSCRIPT C_BaseCombatCharacter::ScriptGetWeapon( int i ) +{ + Assert( i >= 0 && i < MAX_WEAPONS ); + + if ( i < 0 || i >= MAX_WEAPONS ) + return NULL; + + return ToHScript( GetWeapon(i) ); +} + +#endif // MAPBASE_VSCRIPT diff --git a/game/client/c_basecombatcharacter.h b/game/client/c_basecombatcharacter.h index 1d84e4ce7..f580fe46b 100644 --- a/game/client/c_basecombatcharacter.h +++ b/game/client/c_basecombatcharacter.h @@ -29,6 +29,9 @@ class C_BaseCombatCharacter : public C_BaseFlex public: DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif C_BaseCombatCharacter( void ); virtual ~C_BaseCombatCharacter( void ); @@ -99,6 +102,12 @@ class C_BaseCombatCharacter : public C_BaseFlex virtual void GetGlowEffectColor( float *r, float *g, float *b ); #endif // GLOWS_ENABLE +#ifdef MAPBASE_VSCRIPT + int ScriptGetAmmoCount( int i ); + HSCRIPT ScriptGetActiveWeapon(); + HSCRIPT ScriptGetWeapon( int i ); +#endif + public: float m_flNextAttack; diff --git a/game/client/c_basecombatweapon.cpp b/game/client/c_basecombatweapon.cpp index 55d21b391..9f98148a2 100644 --- a/game/client/c_basecombatweapon.cpp +++ b/game/client/c_basecombatweapon.cpp @@ -16,6 +16,9 @@ #include "tier1/KeyValues.h" #include "toolframework/itoolframework.h" #include "toolframework_client.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif // MAPBASE // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -82,6 +85,24 @@ static inline bool ShouldDrawLocalPlayerViewModel( void ) { #if defined( PORTAL ) return false; +#elif MAPBASE + // We shouldn't draw the viewmodel externally. + C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); + if (localplayer) + { + if (localplayer->m_bDrawPlayerModelExternally) + { + // If this isn't the main view, draw the weapon. + view_id_t viewID = CurrentViewID(); + if (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA) + return false; + } + + // Since we already have the local player, check its own ShouldDrawThisPlayer() to avoid extra checks + return !localplayer->ShouldDrawThisPlayer(); + } + else + return false; #else return !C_BasePlayer::ShouldDrawLocalPlayer(); #endif @@ -104,9 +125,15 @@ void C_BaseCombatWeapon::OnRestore() int C_BaseCombatWeapon::GetWorldModelIndex( void ) { +#ifdef MAPBASE + int iIndex = GetOwner() ? m_iWorldModelIndex.Get() : m_iDroppedModelIndex.Get(); +#else + int iIndex = m_iWorldModelIndex.Get(); +#endif // MAPBASE + if ( GameRules() ) { - const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( m_iWorldModelIndex ) ); + const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( iIndex ) ); const char *pTranslatedName = GameRules()->TranslateEffectForVisionFilter( "weapons", pBaseName ); if ( pTranslatedName != pBaseName ) @@ -115,7 +142,7 @@ int C_BaseCombatWeapon::GetWorldModelIndex( void ) } } - return m_iWorldModelIndex; + return iIndex; } //----------------------------------------------------------------------------- @@ -156,11 +183,8 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) } else // weapon carried by other player or not at all { - int overrideModelIndex = CalcOverrideModelIndex(); - if( overrideModelIndex != -1 && overrideModelIndex != GetModelIndex() ) - { - SetModelIndex( overrideModelIndex ); - } + // See comment below + EnsureCorrectRenderingModel(); } UpdateVisibility(); @@ -432,6 +456,12 @@ bool C_BaseCombatWeapon::ShouldDraw( void ) if ( !ShouldDrawLocalPlayerViewModel() ) return true; +#ifdef MAPBASE + // We're drawing this in non-main views, handle it in DrawModel() + if ( pLocalPlayer->m_bDrawPlayerModelExternally ) + return true; +#endif + // don't draw active weapon if not in some kind of 3rd person mode, the viewmodel will do that return false; } @@ -478,15 +508,43 @@ int C_BaseCombatWeapon::DrawModel( int flags ) // check if local player chases owner of this weapon in first person C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); - if ( localplayer && localplayer->IsObserver() && GetOwner() ) + if ( localplayer ) { - // don't draw weapon if chasing this guy as spectator - // we don't check that in ShouldDraw() since this may change - // without notification - - if ( localplayer->GetObserverMode() == OBS_MODE_IN_EYE && - localplayer->GetObserverTarget() == GetOwner() ) - return false; +#ifdef MAPBASE + if (localplayer->m_bDrawPlayerModelExternally) + { + // If this isn't the main view, draw the weapon. + view_id_t viewID = CurrentViewID(); + if ( (!localplayer->InFirstPersonView() || (viewID != VIEW_MAIN && viewID != VIEW_INTRO_CAMERA)) && (viewID != VIEW_SHADOW_DEPTH_TEXTURE || !localplayer->IsEffectActive(EF_DIMLIGHT)) ) + { + // TODO: Is this inefficient? + int nModelIndex = GetModelIndex(); + int nWorldModelIndex = GetWorldModelIndex(); + if (nModelIndex != nWorldModelIndex) + { + SetModelIndex(nWorldModelIndex); + } + + int iDraw = BaseClass::DrawModel(flags); + + if (nModelIndex != nWorldModelIndex) + { + SetModelIndex(nModelIndex); + } + + return iDraw; + } + } +#endif // MAPBASE + if ( localplayer->IsObserver() && GetOwner() ) + { + // don't draw weapon if chasing this guy as spectator + // we don't check that in ShouldDraw() since this may change + // without notification + if ( localplayer->GetObserverMode() == OBS_MODE_IN_EYE && + localplayer->GetObserverTarget() == GetOwner() ) + return false; + } } return BaseClass::DrawModel( flags ); @@ -514,6 +572,34 @@ int C_BaseCombatWeapon::CalcOverrideModelIndex() } } +//----------------------------------------------------------------------------- +// If the local player is visible (thirdperson mode, tf2 taunts, etc., then make sure that we are using the +// w_ (world) model not the v_ (view) model or else the model can flicker, etc. +// Otherwise, if we're not the local player, always use the world model +//----------------------------------------------------------------------------- +void C_BaseCombatWeapon::EnsureCorrectRenderingModel() +{ + C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); + if ( localplayer && + localplayer == GetOwner() && + !ShouldDrawUsingViewModel() ) + { + return; + } + + // BRJ 10/14/02 + // FIXME: Remove when Yahn's client-side prediction is done + // It's a hacky workaround for the model indices fighting + // (GetRenderBounds uses the model index, which is for the view model) + SetModelIndex( GetWorldModelIndex() ); + + // Validate our current sequence just in case ( in theory the view and weapon models should have the same sequences for sequences that overlap at least ) + CStudioHdr *pStudioHdr = GetModelPtr(); + if ( pStudioHdr && GetSequence() >= pStudioHdr->GetNumSeq() ) + { + SetSequence( 0 ); + } +} //----------------------------------------------------------------------------- // tool recording diff --git a/game/client/c_baseentity.cpp b/game/client/c_baseentity.cpp index 9e65f3884..e9a4dbde3 100644 --- a/game/client/c_baseentity.cpp +++ b/game/client/c_baseentity.cpp @@ -40,6 +40,14 @@ #include "cdll_bounded_cvars.h" #include "inetchannelinfo.h" #include "proto_version.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif // MAPBASE +#ifdef MAPBASE_VSCRIPT +#include "vscript_client.h" +#endif // MAPBASE_VSCRIPT + +#include "gamestringpool.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -420,6 +428,143 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst ) RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ), END_RECV_TABLE() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t C_BaseEntity::g_Hook_UpdateOnRemove; +ScriptHook_t C_BaseEntity::g_Hook_ModifyEmitSoundParams; +#endif // MAPBASE_VSCRIPT + +BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRight, "GetRightVector", "Get the right vector of the entity" ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "Gets this entity's team" ) +#endif // MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeft, "GetLeftVector", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeamNumber", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetOrCreatePrivateScriptScope, "Create and retrieve the script-side data associated with an entity" ) + DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) + + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + + 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." ) + + DEFINE_SCRIPTFUNC( GetClassname, "" ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityName, "GetName", "" ) + + DEFINE_SCRIPTFUNC_NAMED( SetAbsOrigin, "SetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) + + DEFINE_SCRIPTFUNC( GetLocalOrigin, "GetLocalOrigin" ) + DEFINE_SCRIPTFUNC( SetLocalOrigin, "SetLocalOrigin" ) + DEFINE_SCRIPTFUNC( GetLocalAngles, "GetLocalAngles" ) + DEFINE_SCRIPTFUNC( SetLocalAngles, "SetLocalAngles" ) + + DEFINE_SCRIPTFUNC_NAMED( WorldSpaceCenter, "GetCenter", "Get vector to center of object - absolute coords" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEyePosition, "EyePosition", "Get vector to eye position - absolute coords" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEyeAngles, "EyeAngles", "Get eye pitch, yaw, roll as a vector" ) + 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" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + + DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetParent, "SetParent", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNextMovePeer, "NextMovePeer", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptFollowEntity, "FollowEntity", "Begin following the specified entity. This makes this entity non-solid, parents it to the target entity, and teleports it to the specified entity's origin. The second parameter is whether or not to use bonemerging while following." ) + DEFINE_SCRIPTFUNC( StopFollowingEntity, "Stops following an entity if we're following one." ) + DEFINE_SCRIPTFUNC( IsFollowingEntity, "Returns true if this entity is following another entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFollowedEntity, "GetFollowedEntity", "Get the entity we're following." ) + + DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetRenderColorVector", "Get the render color as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetRenderColorR", "Get the render color's R value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetRenderColorG", "Get the render color's G value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorB, "GetRenderColorB", "Get the render color's B value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAlpha, "GetRenderAlpha", "Get the render color's alpha value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorVector, "SetRenderColorVector", "Set the render color as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColor, "SetRenderColor", "Set the render color" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorR, "SetRenderColorR", "Set the render color's R value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorG, "SetRenderColorG", "Set the render color's G value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetRenderColorB", "Set the render color's B value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetRenderAlpha", "Set the render color's alpha value" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRenderMode, "GetRenderMode", "Get render mode" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetRenderMode, "SetRenderMode", "Set render mode" ) + + DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( AddEffects, "Add effect(s)" ) + DEFINE_SCRIPTFUNC( RemoveEffects, "Remove effect(s)" ) + DEFINE_SCRIPTFUNC( ClearEffects, "Clear effect(s)" ) + DEFINE_SCRIPTFUNC( SetEffects, "Set effect(s)" ) + DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + + DEFINE_SCRIPTFUNC( GetFlags, "Get flags" ) + DEFINE_SCRIPTFUNC( AddFlag, "Add flag" ) + DEFINE_SCRIPTFUNC( RemoveFlag, "Remove flag" ) + + DEFINE_SCRIPTFUNC( GetEFlags, "Get Eflags" ) + DEFINE_SCRIPTFUNC( AddEFlags, "Add Eflags" ) + DEFINE_SCRIPTFUNC( RemoveEFlags, "Remove Eflags" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveType, "GetMoveType", "Get the move type" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetMoveType, "SetMoveType", "Set the move type" ) + + DEFINE_SCRIPTFUNC( GetCollisionGroup, "Get the collision group" ) + DEFINE_SCRIPTFUNC( SetCollisionGroup, "Set the collision group" ) + + DEFINE_SCRIPTFUNC( GetSolidFlags, "Get solid flags" ) + DEFINE_SCRIPTFUNC( AddSolidFlags, "Add solid flags" ) + DEFINE_SCRIPTFUNC( RemoveSolidFlags, "Remove solid flags" ) + + DEFINE_SCRIPTFUNC( IsPlayer, "Returns true if this entity is a player." ) + DEFINE_SCRIPTFUNC( IsNPC, "Returns true if this entity is a NPC." ) + //DEFINE_SCRIPTFUNC( IsCombatCharacter, "Returns true if this entity is a combat character (player or NPC)." ) + 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." ) + + + DEFINE_SIMPLE_SCRIPTHOOK( C_BaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." ) + + BEGIN_SCRIPTHOOK( C_BaseEntity::g_Hook_ModifyEmitSoundParams, "ModifyEmitSoundParams", FIELD_VOID, "Called every time a sound is emitted on this entity, allowing for its parameters to be modified." ) + DEFINE_SCRIPTHOOK_PARAM( "params", FIELD_HSCRIPT ) + END_SCRIPTHOOK() + +#endif // MAPBASE_VSCRIPT + +END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_PredictableId ) @@ -451,6 +596,10 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt(RECVINFO(m_nRenderMode)), RecvPropInt(RECVINFO(m_nRenderFX)), RecvPropInt(RECVINFO(m_clrRender)), +#ifdef MAPBASE + RecvPropInt(RECVINFO(m_iViewHideFlags)), + RecvPropBool(RECVINFO(m_bDisableFlashlight)), +#endif // MAPBASE RecvPropInt(RECVINFO(m_iTeamNum)), RecvPropInt(RECVINFO(m_CollisionGroup)), RecvPropFloat(RECVINFO(m_flElasticity)), @@ -460,6 +609,8 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ), RecvPropInt( RECVINFO( m_iParentAttachment ) ), + RecvPropString(RECVINFO(m_iName)), + RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ), RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ), RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ), @@ -1092,6 +1243,8 @@ bool C_BaseEntity::Init( int entnum, int iSerialNum ) m_nCreationTick = gpGlobals->tickcount; + m_hScriptInstance = NULL; + return true; } @@ -1198,6 +1351,28 @@ void C_BaseEntity::Term() RemoveFromLeafSystem(); RemoveFromAimEntsList(); + + if ( m_hScriptInstance ) + { +#ifdef MAPBASE_VSCRIPT + if ( m_ScriptScope.IsInitialized() && g_Hook_UpdateOnRemove.CanRunInScope( m_ScriptScope ) ) + { + g_Hook_UpdateOnRemove.Call( m_ScriptScope, NULL, NULL ); + } +#endif + + 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 + } } @@ -1547,6 +1722,11 @@ bool C_BaseEntity::ShouldReceiveProjectedTextures( int flags ) if ( IsEffectActive( EF_NODRAW ) ) return false; +#ifdef MAPBASE + if ( m_bDisableFlashlight ) + return false; +#endif // MAPBASE + if( flags & SHADOW_FLAGS_FLASHLIGHT ) { if ( GetRenderMode() > kRenderNormal && GetRenderColor().a == 0 ) @@ -1981,6 +2161,17 @@ int C_BaseEntity::DrawModel( int flags ) return drawn; } +#ifdef MAPBASE + if (m_iViewHideFlags > 0) + { + // Hide this entity if it's not supposed to be drawn in this view. + if (m_iViewHideFlags & (1 << CurrentViewID())) + { + return 0; + } + } +#endif + int modelType = modelinfo->GetModelType( model ); switch ( modelType ) { @@ -4743,11 +4934,13 @@ C_BaseEntity *C_BaseEntity::Instance( int iEnt ) return ClientEntityList().GetBaseEntity( iEnt ); } -#ifdef WIN32 +#if defined(WIN32) && _MSC_VER < 1900 #pragma warning( push ) #include #pragma warning( pop ) -#endif +#else +#include +#endif // WIN32 //----------------------------------------------------------------------------- // Purpose: @@ -5982,6 +6175,9 @@ BEGIN_DATADESC_NO_BASE( C_BaseEntity ) DEFINE_FIELD( m_angAbsRotation, FIELD_VECTOR ), DEFINE_ARRAY( m_rgflCoordinateFrame, FIELD_FLOAT, 12 ), // NOTE: MUST BE IN LOCAL SPACE, NOT POSITION_VECTOR!!! (see CBaseEntity::Restore) DEFINE_FIELD( m_fFlags, FIELD_INTEGER ), +#ifdef MAPBASE_VSCRIPT + DEFINE_FIELD( m_iszScriptId, FIELD_STRING ), +#endif // MAPBASE_VSCRIPT END_DATADESC() //----------------------------------------------------------------------------- @@ -6426,6 +6622,187 @@ int C_BaseEntity::GetCreationTick() const return m_nCreationTick; } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Using my edict, cook up a unique VScript scope that's private to me, and +// persistent. +//----------------------------------------------------------------------------- +bool C_BaseEntity::ValidateScriptScope() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + // Force instance creation + GetScriptInstance(); + + EHANDLE hThis; + hThis.Set(this); + + bool bResult = m_ScriptScope.Init(STRING(m_iszScriptId)); + + if (!bResult) + { + DevMsg("%s couldn't create ScriptScope!\n", GetDebugName()); + return false; + } + g_pScriptVM->SetValue(m_ScriptScope, "self", GetScriptInstance()); + } + return true; +} + +//----------------------------------------------------------------------------- +// Returns true if the function was located and called. false otherwise. +// NOTE: Assumes the function takes no parameters at the moment. +//----------------------------------------------------------------------------- +bool C_BaseEntity::CallScriptFunction( const char* pFunctionName, ScriptVariant_t* pFunctionReturn ) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + + HSCRIPT hFunc = m_ScriptScope.LookupFunction(pFunctionName); + + if (hFunc) + { + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Gets a function handle +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::LookupScriptFunction( const char* pFunctionName ) +{ + if (!m_ScriptScope.IsInitialized()) + { + return NULL; + } + + return m_ScriptScope.LookupFunction(pFunctionName); +} + +//----------------------------------------------------------------------------- +// Calls and releases a function handle (ASSUMES SCRIPT SCOPE AND FUNCTION ARE VALID!) +//----------------------------------------------------------------------------- +bool C_BaseEntity::CallScriptFunctionHandle( HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn ) +{ + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Load, compile, and run a script file from disk. +// Input : *pScriptFile - The filename of the script file. +// bUseRootScope - If true, runs this script in the root scope, not +// in this entity's private scope. +//----------------------------------------------------------------------------- +bool C_BaseEntity::RunScriptFile( const char* pScriptFile, bool bUseRootScope ) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (bUseRootScope) + { + return VScriptRunScript(pScriptFile); + } + else + { + return VScriptRunScript(pScriptFile, m_ScriptScope, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Compile and execute a discrete string of script source code +// Input : *pScriptText - A string containing script code to compile and run +//----------------------------------------------------------------------------- +bool C_BaseEntity::RunScript( const char* pScriptText, const char* pDebugFilename ) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (m_ScriptScope.Run(pScriptText, pDebugFilename) == SCRIPT_ERROR) + { + DevWarning(" Entity %s encountered an error in RunScript()\n", GetDebugName()); + } + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetMoveParent( void ) +{ + return ToHScript( GetMoveParent() ); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript( GetRootMoveParent() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptFirstMoveChild( void ) +{ + return ToHScript( FirstMoveChild() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptNextMovePeer( void ) +{ + return ToHScript( NextMovePeer() ); +} +#endif // MAPBASE_VSCRIPT + //------------------------------------------------------------------------------ void CC_CL_Find_Ent( const CCommand& args ) { diff --git a/game/client/c_baseentity.h b/game/client/c_baseentity.h index c62b732f0..471489f31 100644 --- a/game/client/c_baseentity.h +++ b/game/client/c_baseentity.h @@ -36,6 +36,11 @@ #include "toolframework/itoolentity.h" #include "tier0/threadtools.h" +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#include "vscript_shared.h" +#endif // MAPBASE_VSCRIPT + class C_Team; class IPhysicsObject; class IClientVehicle; @@ -77,7 +82,7 @@ enum CollideType_t class VarMapEntry_t { - + public: unsigned short type; @@ -99,7 +104,7 @@ struct VarMapping_t float m_lastInterpolationTime; }; - + #define DECLARE_INTERPOLATION() @@ -158,6 +163,15 @@ struct thinkfunc_t int m_nLastThinkTick; }; +#ifdef MAPBASE_VSCRIPT +struct scriptthinkfunc_t +{ + float m_flNextThink; + HSCRIPT m_hfnThink; + unsigned m_iContextHash; +}; +#endif + #define CREATE_PREDICTED_ENTITY( className ) \ C_BaseEntity::CreatePredictedEntityByName( className, __FILE__, __LINE__ ); @@ -183,18 +197,20 @@ class C_BaseEntity : public IClientEntity DECLARE_DATADESC(); DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); C_BaseEntity(); virtual ~C_BaseEntity(); static C_BaseEntity *CreatePredictedEntityByName( const char *classname, const char *module, int line, bool persist = false ); - + // FireBullets uses shared code for prediction. virtual void FireBullets( const FireBulletsInfo_t &info ); virtual void ModifyFireBulletsDamage( CTakeDamageInfo* dmgInfo ) {} virtual bool ShouldDrawUnderwaterBulletBubbles(); virtual bool ShouldDrawWaterImpacts( void ) { return true; } - virtual bool HandleShotImpactingWater( const FireBulletsInfo_t &info, + virtual bool HandleShotImpactingWater( const FireBulletsInfo_t &info, const Vector &vecEnd, ITraceFilter *pTraceFilter, Vector *pVecTracerDest ); virtual ITraceFilter* GetBeamTraceFilter( void ); virtual void DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator = NULL ); @@ -227,10 +243,10 @@ class C_BaseEntity : public IClientEntity void Interp_SetupMappings( VarMapping_t *map ); - + // Returns 1 if there are no more changes (ie: we could call RemoveFromInterpolationList). int Interp_Interpolate( VarMapping_t *map, float currentTime ); - + void Interp_RestoreToLastNetworked( VarMapping_t *map ); void Interp_UpdateInterpolationAmounts( VarMapping_t *map ); void Interp_HierarchyUpdateInterpolationAmounts(); @@ -256,6 +272,35 @@ class C_BaseEntity : public IClientEntity string_t m_iClassname; +#ifdef MAPBASE_VSCRIPT + // VSCRIPT + bool ValidateScriptScope(); + bool CallScriptFunction( const char* pFunctionName, ScriptVariant_t* pFunctionReturn ); + + HSCRIPT GetOrCreatePrivateScriptScope(); + HSCRIPT GetScriptScope() { return m_ScriptScope; } + + HSCRIPT LookupScriptFunction(const char* pFunctionName); + bool CallScriptFunctionHandle(HSCRIPT hFunc, ScriptVariant_t* pFunctionReturn); + + bool RunScriptFile( const char* pScriptFile, bool bUseRootScope = false ); + bool RunScript( const char* pScriptText, const char* pDebugFilename = "C_BaseEntity::RunScript" ); +#endif + + HSCRIPT GetScriptOwnerEntity(); + virtual void SetScriptOwnerEntity(HSCRIPT pOwner); + + HSCRIPT GetScriptInstance(); + + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; +#ifdef MAPBASE_VSCRIPT + CScriptScope m_ScriptScope; + + static ScriptHook_t g_Hook_UpdateOnRemove; + static ScriptHook_t g_Hook_ModifyEmitSoundParams; +#endif + // IClientUnknown overrides. public: @@ -356,10 +401,15 @@ class C_BaseEntity : public IClientEntity bool IsMarkedForDeletion( void ); virtual int entindex( void ) const; - + +#ifdef MAPBASE_VSCRIPT + // "I don't know why but wrapping entindex() works, while calling it directly crashes." + inline int GetEntityIndex() const { return entindex(); } +#endif + // This works for client-only entities and returns the GetEntryIndex() of the entity's handle, // so the sound system can get an IClientEntity from it. - int GetSoundSourceIndex() const; + int GetSoundSourceIndex() const; // Server to client message received virtual void ReceiveMessage( int classID, bf_read &msg ); @@ -391,7 +441,7 @@ class C_BaseEntity : public IClientEntity CParticleProperty *ParticleProp(); const CParticleProperty *ParticleProp() const; - // Simply here for game shared + // Simply here for game shared bool IsFloating(); virtual bool ShouldSavePhysics(); @@ -411,7 +461,7 @@ class C_BaseEntity : public IClientEntity int RestoreDataDescBlock( IRestore &restore, datamap_t *dmap ); // Called after restoring data into prediction slots. This function is used in place of proxies - // on the variables, so if some variable like m_nModelIndex needs to update other state (like + // on the variables, so if some variable like m_nModelIndex needs to update other state (like // the model pointer), it is done here. void OnPostRestoreData(); @@ -509,8 +559,8 @@ class C_BaseEntity : public IClientEntity const Vector& WorldAlignSize( ) const; bool IsPointSized() const; - // Returns a radius of a sphere - // *centered at the world space center* bounding the collision representation + // Returns a radius of a sphere + // *centered at the world space center* bounding the collision representation // of the entity. NOTE: The world space center *may* move when the entity rotates. float BoundingRadius() const; @@ -528,7 +578,7 @@ class C_BaseEntity : public IClientEntity // This function gets your parent's transform. If you're parented to an attachment, // this calculates the attachment's transform and gives you that. // - // You must pass in tempMatrix for scratch space - it may need to fill that in and return it instead of + // You must pass in tempMatrix for scratch space - it may need to fill that in and return it instead of // pointing you right at a variable in your parent. matrix3x4_t& GetParentToWorldTransform( matrix3x4_t &tempMatrix ); @@ -611,7 +661,7 @@ class C_BaseEntity : public IClientEntity static HSOUNDSCRIPTHANDLE PrecacheScriptSound( const char *soundname ); static void PrefetchScriptSound( const char *soundname ); - // For each client who appears to be a valid recipient, checks the client has disabled CC and if so, removes them from + // For each client who appears to be a valid recipient, checks the client has disabled CC and if so, removes them from // the recipient list. static void RemoveRecipientsIfNotCloseCaptioning( C_RecipientFilter& filter ); static void EmitCloseCaption( IRecipientFilter& filter, int entindex, char const *token, CUtlVector< Vector >& soundorigins, float duration, bool warnifmissing = false ); @@ -632,20 +682,20 @@ class C_BaseEntity : public IClientEntity void UpdatePartitionListEntry(); - // This can be used to setup the entity as a client-only entity. + // This can be used to setup the entity as a client-only entity. // Override this to perform per-entity clientside setup virtual bool InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup ); - + // This function gets called on all client entities once per simulation phase. // It dispatches events like OnDataChanged(), and calls the legacy function AddEntity(). - virtual void Simulate(); + virtual void Simulate(); - // This event is triggered during the simulation phase if an entity's data has changed. It is + // This event is triggered during the simulation phase if an entity's data has changed. It is // better to hook this instead of PostDataUpdate() because in PostDataUpdate(), server entity origins // are incorrect and attachment points can't be used. virtual void OnDataChanged( DataUpdateType_t type ); @@ -656,13 +706,13 @@ class C_BaseEntity : public IClientEntity bool IsStandable() const; bool IsBSPModel() const; - + // If this is a vehicle, returns the vehicle interface virtual IClientVehicle* GetClientVehicle() { return NULL; } // Returns the aiment render origin + angles virtual void GetAimEntOrigin( IClientEntity *pAttachedTo, Vector *pAbsOrigin, QAngle *pAbsAngles ); - + // get network origin from previous update virtual const Vector& GetOldOrigin(); @@ -687,7 +737,7 @@ class C_BaseEntity : public IClientEntity virtual bool ShouldDraw(); inline bool IsVisible() const { return m_hRender != INVALID_CLIENT_RENDER_HANDLE; } void UpdateVisibility(); - + // Returns true if the entity changes its position every frame on the server but it doesn't // set animtime. In that case, the client returns true here so it copies the server time to // animtime in OnDataChanged and the position history is correct for interpolation. @@ -709,7 +759,7 @@ class C_BaseEntity : public IClientEntity void Interp_Reset( VarMapping_t *map ); virtual void ResetLatched(); - + float GetInterpolationAmount( int flags ); float GetLastChangeTime( int flags ); @@ -765,7 +815,7 @@ class C_BaseEntity : public IClientEntity void AddToLeafSystem(); void AddToLeafSystem( RenderGroup_t group ); // remove entity form leaf system again - void RemoveFromLeafSystem(); + void RemoveFromLeafSystem(); // A method to apply a decal to an entity virtual void AddDecal( const Vector& rayStart, const Vector& rayEnd, @@ -841,7 +891,7 @@ class C_BaseEntity : public IClientEntity } void PhysicsDispatchThink( BASEPTR thinkFunc ); - + // Toggle the visualization of the entity's abs/bbox enum { @@ -858,7 +908,8 @@ class C_BaseEntity : public IClientEntity void SetSize( const Vector &vecMin, const Vector &vecMax ); // UTIL_SetSize( pev, mins, maxs ); char const *GetClassname( void ); char const *GetDebugName( void ); - static int PrecacheModel( const char *name ); + virtual const char *GetPlayerName() const { return NULL; } + static int PrecacheModel( const char *name ); static bool PrecacheSound( const char *name ); static void PrefetchSound( const char *name ); void Remove( ); // UTIL_Remove( this ); @@ -900,7 +951,7 @@ class C_BaseEntity : public IClientEntity virtual bool IsCurrentlyTouching( void ) const; virtual void StartTouch( C_BaseEntity *pOther ); - virtual void Touch( C_BaseEntity *pOther ); + virtual void Touch( C_BaseEntity *pOther ); virtual void EndTouch( C_BaseEntity *pOther ); void (C_BaseEntity ::*m_pfnTouch)( C_BaseEntity *pOther ); @@ -984,7 +1035,7 @@ class C_BaseEntity : public IClientEntity // Unlinks from hierarchy // Set the movement parent. Your local origin and angles will become relative to this parent. - // If iAttachment is a valid attachment on the parent, then your local origin and angles + // If iAttachment is a valid attachment on the parent, then your local origin and angles // are relative to the attachment on this entity. void SetParent( C_BaseEntity *pParentEntity, int iParentAttachment=0 ); @@ -1000,10 +1051,12 @@ class C_BaseEntity : public IClientEntity ///////////////// virtual bool IsPlayer( void ) const { return false; }; + + virtual bool IsBot( void ) const { return ((GetFlags() & FL_FAKECLIENT) == FL_FAKECLIENT) ? true : false; } virtual bool IsBaseCombatCharacter( void ) { return false; }; virtual C_BaseCombatCharacter *MyCombatCharacterPointer( void ) { return NULL; } virtual bool IsNPC( void ) { return false; } - C_AI_BaseNPC *MyNPCPointer( void ); + C_AI_BaseNPC *MyNPCPointer( void ); virtual bool IsNextBot() { return false; } // TF2 specific virtual bool IsBaseObject( void ) const { return false; } @@ -1017,7 +1070,12 @@ class C_BaseEntity : public IClientEntity virtual Vector EyePosition( void ); virtual const QAngle& EyeAngles( void ); // Direction of eyes virtual const QAngle& LocalEyeAngles( void ); // Direction of eyes in local space (pl.v_angle) - + +#ifdef MAPBASE + // Created for script_intro and info_player_view_proxy + virtual void GetEyePosition( Vector &vecOrigin, QAngle &angAngles ) { vecOrigin = EyePosition(); angAngles = EyeAngles(); } +#endif + // position of ears virtual Vector EarPosition( void ); @@ -1035,7 +1093,7 @@ class C_BaseEntity : public IClientEntity void SetGravity( float flGravity ); float GetGravity( void ) const; - // Sets the model from a model index + // Sets the model from a model index void SetModelByIndex( int nModelIndex ); // Set model... (NOTE: Should only be used by client-only entities @@ -1110,10 +1168,66 @@ class C_BaseEntity : public IClientEntity bool IsFollowingEntity(); CBaseEntity *GetFollowedEntity(); +#ifdef MAPBASE_VSCRIPT + void ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ); + HSCRIPT ScriptGetFollowedEntity(); +#endif + // For shadows rendering the correct body + sequence... virtual int GetBody() { return 0; } virtual int GetSkin() { return 0; } + // halotroop2288: Removed ifdef on -Right for simplicity. + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } + const Vector& ScriptGetRight(void) { static Vector vecRight; GetVectors(NULL, &vecRight, NULL); return vecRight; } + const Vector& ScriptGetLeft(void) { static Vector vecRight; GetVectors(NULL, &vecRight, NULL); return vecRight; } + const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } + +#ifdef MAPBASE_VSCRIPT + const char* ScriptGetModelName( void ) const { return STRING(GetModelName()); } + + void ScriptStopSound(const char* soundname); + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + void VScriptPrecacheScriptSound(const char* soundname); + + const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } + const QAngle& ScriptEyeAngles(void) { static QAngle ang; ang = EyeAngles(); return ang; } + void ScriptSetForward( const Vector& v ) { QAngle angles; VectorAngles( v, angles ); SetAbsAngles( angles ); } + + const Vector& ScriptGetBoundingMins( void ) { return m_Collision.OBBMins(); } + const Vector& ScriptGetBoundingMaxs( void ) { return m_Collision.OBBMaxs(); } + + HSCRIPT ScriptEntityToWorldTransform( void ); + + HSCRIPT ScriptGetPhysicsObject( void ); + + void ScriptSetParent( HSCRIPT hParent, const char *szAttachment ); + HSCRIPT ScriptGetMoveParent( void ); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild( void ); + HSCRIPT ScriptNextMovePeer( void ); + + const Vector& ScriptGetColorVector(); + int ScriptGetColorR() { return m_clrRender.GetR(); } + int ScriptGetColorG() { return m_clrRender.GetG(); } + int ScriptGetColorB() { return m_clrRender.GetB(); } + int ScriptGetAlpha() { return m_clrRender.GetA(); } + void ScriptSetColorVector( const Vector& vecColor ); + void ScriptSetColor( int r, int g, int b ); + void ScriptSetColorR( int iVal ) { SetRenderColorR( iVal ); } + void ScriptSetColorG( int iVal ) { SetRenderColorG( iVal ); } + void ScriptSetColorB( int iVal ) { SetRenderColorB( iVal ); } + void ScriptSetAlpha( int iVal ) { SetRenderColorA( iVal ); } + + int ScriptGetRenderMode() { return GetRenderMode(); } + void ScriptSetRenderMode( int nRenderMode ) { SetRenderMode( (RenderMode_t)nRenderMode ); } + + int ScriptGetMoveType() { return GetMoveType(); } + void ScriptSetMoveType( int iMoveType ) { SetMoveType( (MoveType_t)iMoveType ); } +#endif // MAPBASE_VSCRIPT + // Stubs on client void NetworkStateManualMode( bool activate ) { } void NetworkStateChanged() { } @@ -1129,7 +1243,7 @@ class C_BaseEntity : public IClientEntity float GetLastThink( const char *szContext = NULL ); int GetNextThinkTick( const char *szContext = NULL ); int GetLastThinkTick( const char *szContext = NULL ); - + // These set entity flags (EFL_*) to help optimize queries void CheckHasThinkFunction( bool isThinkingHint = false ); void CheckHasGamePhysicsSimulation(); @@ -1151,11 +1265,11 @@ class C_BaseEntity : public IClientEntity #ifdef _DEBUG void FunctionCheck( void *pFunction, const char *name ); - ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) - { + ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, const char *name ) + { //COMPILE_TIME_ASSERT( sizeof(func) == 4 ); - m_pfnTouch = func; - //FunctionCheck( *(reinterpret_cast(&m_pfnTouch)), name ); + m_pfnTouch = func; + //FunctionCheck( *(reinterpret_cast(&m_pfnTouch)), name ); return func; } #endif @@ -1238,10 +1352,10 @@ class C_BaseEntity : public IClientEntity static void PushEnableAbsRecomputations( bool bEnable ); static void PopEnableAbsRecomputations(); - // This requires the abs recomputation stack to be empty and just sets the global state. + // This requires the abs recomputation stack to be empty and just sets the global state. // It should only be used at the scope of the frame loop. - static void EnableAbsRecomputations( bool bEnable ); - + static void EnableAbsRecomputations( bool bEnable ); + static bool IsAbsRecomputationsEnabled( void ); @@ -1261,10 +1375,12 @@ class C_BaseEntity : public IClientEntity void SetRenderMode( RenderMode_t nRenderMode, bool bForceUpdate = false ); RenderMode_t GetRenderMode() const; -public: + const char* GetEntityName(); + +public: // Determine what entity this corresponds to - int index; + int index; // Render information unsigned char m_nRenderFX; @@ -1275,8 +1391,13 @@ class C_BaseEntity : public IClientEntity CNetworkColor32( m_clrRender ); +#ifdef MAPBASE + int m_iViewHideFlags; + bool m_bDisableFlashlight; +#endif + private: - + // Model for rendering const model_t *model; @@ -1287,7 +1408,7 @@ class C_BaseEntity : public IClientEntity float m_flSimulationTime; float m_flOldSimulationTime; - + float m_flCreateTime; byte m_ubInterpolationFrame; @@ -1313,7 +1434,7 @@ class C_BaseEntity : public IClientEntity // Should we interpolate this tick? (Used to be EF_NOINTERP) bool IsNoInterpolationFrame(); - // + // int m_nNextThinkTick; int m_nLastThinkTick; @@ -1342,7 +1463,7 @@ class C_BaseEntity : public IClientEntity #endif // used so we know when things are no longer touching - int touchStamp; + int touchStamp; // Called after predicted entity has been acknowledged so that no longer needed entity can // be deleted @@ -1400,7 +1521,7 @@ class C_BaseEntity : public IClientEntity protected: // pointer to the entity's physics object (vphysics.dll) - IPhysicsObject *m_pPhysicsObject; + IPhysicsObject *m_pPhysicsObject; #if !defined( NO_ENTITY_PREDICTION ) bool m_bPredictionEligible; @@ -1413,12 +1534,21 @@ class C_BaseEntity : public IClientEntity CUtlVector< thinkfunc_t > m_aThinkFunctions; int m_iCurrentThinkContext; +#ifdef MAPBASE_VSCRIPT +public: + void ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float time ); + void ScriptContextThink(); +private: + CUtlVector< scriptthinkfunc_t* > m_ScriptThinkFuncs; +public: +#endif + // Object eye position Vector m_vecViewOffset; #if defined(SIXENSE) Vector m_vecEyeOffset; - QAngle m_EyeAngleOffset; + QAngle m_EyeAngleOffset; #endif // Allow studio models to tell us what their m_nBody value is virtual int GetStudioBody( void ) { return 0; } @@ -1434,7 +1564,7 @@ class C_BaseEntity : public IClientEntity // Figure out the smoothly interpolated origin for all server entities. Happens right before // letting all entities simulate. static void InterpolateServerEntities(); - + // Check which entities want to be drawn and add them to the leaf system. static void AddVisibleEntities(); @@ -1488,7 +1618,7 @@ class C_BaseEntity : public IClientEntity // FIXME: REMOVE!!! void MoveToAimEnt( ); - + // Sets/Gets the next think based on context index void SetNextThink( int nContextIndex, float thinkTime ); void SetLastThink( int nContextIndex, float thinkTime ); @@ -1522,7 +1652,7 @@ class C_BaseEntity : public IClientEntity // Base velocity Vector m_vecBaseVelocity; - + // Gravity multiplier float m_flGravity; @@ -1580,7 +1710,7 @@ class C_BaseEntity : public IClientEntity // Friction. - float m_flFriction; + float m_flFriction; Vector m_vecAbsOrigin; @@ -1639,7 +1769,9 @@ class C_BaseEntity : public IClientEntity // The owner! EHANDLE m_hOwnerEntity; EHANDLE m_hEffectEntity; - + + char m_iName[MAX_PATH]; + // This is a random seed used by the networking code to allow client - side prediction code // randon number generators to spit out the same random numbers on both sides for a particular // usercmd input. @@ -1649,13 +1781,13 @@ class C_BaseEntity : public IClientEntity static bool s_bAbsRecomputationEnabled; static bool s_bInterpolate; - + int m_fDataObjectTypes; AimEntsListHandle_t m_AimEntsListHandle; int m_nCreationTick; - + public: float m_fRenderingClipPlane[4]; //world space clip plane when drawing bool m_bEnableRenderingClipPlane; //true to use the custom clip plane when drawing @@ -1666,7 +1798,7 @@ class C_BaseEntity : public IClientEntity void AddToInterpolationList(); void RemoveFromInterpolationList(); unsigned short m_InterpolationListEntry; // Entry into g_InterpolationList (or g_InterpolationList.InvalidIndex if not in the list). - + void AddToTeleportList(); void RemoveFromTeleportList(); unsigned short m_TeleportListEntry; @@ -1692,12 +1824,12 @@ class C_BaseEntity : public IClientEntity EXTERN_RECV_TABLE(DT_BaseEntity); inline bool FClassnameIs( C_BaseEntity *pEntity, const char *szClassname ) -{ +{ Assert( pEntity ); if ( pEntity == NULL ) return false; - return !strcmp( pEntity->GetClassname(), szClassname ) ? true : false; + return !strcmp( pEntity->GetClassname(), szClassname ) ? true : false; } #define SetThink( a ) ThinkSet( static_cast (a), 0, NULL ) @@ -1750,17 +1882,17 @@ inline bool C_BaseEntity::IsServerEntity( void ) // Inline methods //----------------------------------------------------------------------------- inline matrix3x4_t &C_BaseEntity::EntityToWorldTransform() -{ +{ Assert( s_bAbsQueriesValid ); CalcAbsolutePosition(); - return m_rgflCoordinateFrame; + return m_rgflCoordinateFrame; } inline const matrix3x4_t &C_BaseEntity::EntityToWorldTransform() const { Assert( s_bAbsQueriesValid ); const_cast(this)->CalcAbsolutePosition(); - return m_rgflCoordinateFrame; + return m_rgflCoordinateFrame; } inline const Vector& C_BaseEntity::GetNetworkOrigin() const @@ -1928,25 +2060,25 @@ inline bool CBaseEntity::IsPointSized() const //----------------------------------------------------------------------------- inline C_BaseEntity *C_BaseEntity::GetMoveParent( void ) const { - return m_pMoveParent; + return m_pMoveParent; } inline C_BaseEntity *C_BaseEntity::FirstMoveChild( void ) const { - return m_pMoveChild; + return m_pMoveChild; } inline C_BaseEntity *C_BaseEntity::NextMovePeer( void ) const { - return m_pMovePeer; + return m_pMovePeer; } //----------------------------------------------------------------------------- // Velocity //----------------------------------------------------------------------------- -inline const Vector& C_BaseEntity::GetLocalVelocity() const -{ - return m_vecVelocity; +inline const Vector& C_BaseEntity::GetLocalVelocity() const +{ + return m_vecVelocity; } inline const QAngle& C_BaseEntity::GetLocalAngularVelocity( ) const @@ -1954,29 +2086,29 @@ inline const QAngle& C_BaseEntity::GetLocalAngularVelocity( ) const return m_vecAngVelocity; } -inline const Vector& C_BaseEntity::GetBaseVelocity() const -{ - return m_vecBaseVelocity; +inline const Vector& C_BaseEntity::GetBaseVelocity() const +{ + return m_vecBaseVelocity; } -inline void C_BaseEntity::SetBaseVelocity( const Vector& v ) -{ - m_vecBaseVelocity = v; +inline void C_BaseEntity::SetBaseVelocity( const Vector& v ) +{ + m_vecBaseVelocity = v; } -inline void C_BaseEntity::SetFriction( float flFriction ) -{ - m_flFriction = flFriction; +inline void C_BaseEntity::SetFriction( float flFriction ) +{ + m_flFriction = flFriction; } -inline void C_BaseEntity::SetGravity( float flGravity ) -{ - m_flGravity = flGravity; +inline void C_BaseEntity::SetGravity( float flGravity ) +{ + m_flGravity = flGravity; } -inline float C_BaseEntity::GetGravity( void ) const -{ - return m_flGravity; +inline float C_BaseEntity::GetGravity( void ) const +{ + return m_flGravity; } inline int C_BaseEntity::GetWaterLevel() const @@ -1989,9 +2121,9 @@ inline void C_BaseEntity::SetWaterLevel( int nLevel ) m_nWaterLevel = nLevel; } -inline float C_BaseEntity::GetElasticity( void ) const -{ - return m_flElasticity; +inline float C_BaseEntity::GetElasticity( void ) const +{ + return m_flElasticity; } inline const color32 CBaseEntity::GetRenderColor() const @@ -2039,9 +2171,9 @@ inline RenderMode_t CBaseEntity::GetRenderMode() const //----------------------------------------------------------------------------- // checks to see if the entity is marked for deletion //----------------------------------------------------------------------------- -inline bool C_BaseEntity::IsMarkedForDeletion( void ) -{ - return (m_iEFlags & EFL_KILLME); +inline bool C_BaseEntity::IsMarkedForDeletion( void ) +{ + return (m_iEFlags & EFL_KILLME); } inline void C_BaseEntity::AddEFlags( int nEFlagMask ) @@ -2064,9 +2196,9 @@ inline unsigned char CBaseEntity::GetParentAttachment() const return m_iParentAttachment; } -inline ClientRenderHandle_t CBaseEntity::GetRenderHandle() const -{ - return m_hRender; +inline ClientRenderHandle_t CBaseEntity::GetRenderHandle() const +{ + return m_hRender; } inline ClientRenderHandle_t& CBaseEntity::RenderHandle() @@ -2076,24 +2208,24 @@ inline ClientRenderHandle_t& CBaseEntity::RenderHandle() #ifdef SIXENSE -inline const Vector& CBaseEntity::GetEyeOffset() const -{ - return m_vecEyeOffset; +inline const Vector& CBaseEntity::GetEyeOffset() const +{ + return m_vecEyeOffset; } -inline void CBaseEntity::SetEyeOffset( const Vector& v ) -{ - m_vecEyeOffset = v; +inline void CBaseEntity::SetEyeOffset( const Vector& v ) +{ + m_vecEyeOffset = v; } -inline const QAngle & CBaseEntity::GetEyeAngleOffset() const -{ - return m_EyeAngleOffset; +inline const QAngle & CBaseEntity::GetEyeAngleOffset() const +{ + return m_EyeAngleOffset; } -inline void CBaseEntity::SetEyeAngleOffset( const QAngle & qa ) -{ - m_EyeAngleOffset = qa; +inline void CBaseEntity::SetEyeAngleOffset( const QAngle & qa ) +{ + m_EyeAngleOffset = qa; } #endif @@ -2143,8 +2275,8 @@ inline bool C_BaseEntity::IsNoInterpolationFrame() } //----------------------------------------------------------------------------- -// Purpose: -// Input : handle - +// Purpose: +// Input : handle - // Output : inline void //----------------------------------------------------------------------------- inline void C_BaseEntity::SetToolHandle( HTOOLHANDLE handle ) @@ -2155,8 +2287,8 @@ inline void C_BaseEntity::SetToolHandle( HTOOLHANDLE handle ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : inline HTOOLHANDLE //----------------------------------------------------------------------------- inline HTOOLHANDLE C_BaseEntity::GetToolHandle() const @@ -2169,7 +2301,7 @@ inline HTOOLHANDLE C_BaseEntity::GetToolHandle() const } //----------------------------------------------------------------------------- -// +// //----------------------------------------------------------------------------- inline bool C_BaseEntity::IsEnabledInToolView() const { @@ -2181,8 +2313,8 @@ inline bool C_BaseEntity::IsEnabledInToolView() const } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : inline bool //----------------------------------------------------------------------------- inline bool C_BaseEntity::ShouldRecordInTools() const @@ -2194,6 +2326,11 @@ inline bool C_BaseEntity::ShouldRecordInTools() const #endif } +inline const char *C_BaseEntity::GetEntityName() +{ + return m_iName; +} + C_BaseEntity *CreateEntityByName( const char *className ); #endif // C_BASEENTITY_H diff --git a/game/client/c_baseflex.cpp b/game/client/c_baseflex.cpp index b18168401..fdeb1ba77 100644 --- a/game/client/c_baseflex.cpp +++ b/game/client/c_baseflex.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ //===========================================================================// @@ -117,8 +117,8 @@ bool GetHWMExpressionFileName( const char *pFilename, char *pHWMFilename ) return true; } -C_BaseFlex::C_BaseFlex() : - m_iv_viewtarget( "C_BaseFlex::m_iv_viewtarget" ), +C_BaseFlex::C_BaseFlex() : + m_iv_viewtarget( "C_BaseFlex::m_iv_viewtarget" ), m_iv_flexWeight("C_BaseFlex:m_iv_flexWeight" ), #ifdef HL2_CLIENT_DLL m_iv_vecLean("C_BaseFlex:m_iv_vecLean" ), @@ -192,7 +192,7 @@ void C_BaseFlex::SetupMappings( char const *pchFileRoot ) CStudioHdr *C_BaseFlex::OnNewModel() { CStudioHdr *hdr = BaseClass::OnNewModel(); - + // init to invalid setting m_iBlink = -1; m_iEyeUpdown = LocalFlexController_t(-1); @@ -288,7 +288,7 @@ bool C_BaseFlex::GetSoundSpatialization( SpatializationInfo_t& info ) { Vector origin; QAngle angles; - + C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false ); if (GetAttachment( m_iMouthAttachment, origin, angles )) @@ -311,7 +311,7 @@ bool C_BaseFlex::GetSoundSpatialization( SpatializationInfo_t& info ) //----------------------------------------------------------------------------- -// Purpose: run the interpreted FAC's expressions, converting global flex_controller +// Purpose: run the interpreted FAC's expressions, converting global flex_controller // values into FAC weights //----------------------------------------------------------------------------- void C_BaseFlex::RunFlexRules( CStudioHdr *hdr, float *dest ) @@ -337,7 +337,7 @@ void C_BaseFlex::RunFlexRules( CStudioHdr *hdr, float *dest ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool CFlexSceneFileManager::Init() { @@ -353,7 +353,7 @@ bool CFlexSceneFileManager::Init() #if defined( TF_CLIENT_DLL ) // HACK TO ALL TF TO HAVE PER CLASS OVERRIDES - char const *pTFClasses[] = + char const *pTFClasses[] = { "scout", "sniper", @@ -411,8 +411,8 @@ void CFlexSceneFileManager::Shutdown() //----------------------------------------------------------------------------- // Purpose: Sets up translations -// Input : *instance - -// *pSettinghdr - +// Input : *instance - +// *pSettinghdr - // Output : void //----------------------------------------------------------------------------- void CFlexSceneFileManager::EnsureTranslations( IHasLocalToGlobalFlexSettings *instance, const flexsettinghdr_t *pSettinghdr ) @@ -425,15 +425,15 @@ void CFlexSceneFileManager::EnsureTranslations( IHasLocalToGlobalFlexSettings *i } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void *CFlexSceneFileManager::FindSceneFile( IHasLocalToGlobalFlexSettings *instance, const char *filename, bool allowBlockingIO ) { char szFilename[MAX_PATH]; Assert( V_strlen( filename ) < MAX_PATH ); V_strcpy( szFilename, filename ); - -#if defined( TF_CLIENT_DLL ) + +#if defined( TF_CLIENT_DLL ) char szHWMFilename[MAX_PATH]; if ( GetHWMExpressionFileName( szFilename, szHWMFilename ) ) { @@ -455,7 +455,7 @@ void *CFlexSceneFileManager::FindSceneFile( IHasLocalToGlobalFlexSettings *insta return file->buffer; } } - + if ( !allowBlockingIO ) { return NULL; @@ -491,7 +491,7 @@ void *CFlexSceneFileManager::FindSceneFile( IHasLocalToGlobalFlexSettings *insta for ( int i = 0; i < pHdr->numflexsettings; ++i, ++pFlexSetting ) { swap.SwapFieldsToTargetEndian( pFlexSetting ); - + flexweight_t *pWeight = (flexweight_t*)(((byte*)pFlexSetting) + pFlexSetting->settingindex ); for ( int j = 0; j < pFlexSetting->numsettings; ++j, ++pWeight ) { @@ -520,7 +520,7 @@ void *CFlexSceneFileManager::FindSceneFile( IHasLocalToGlobalFlexSettings *insta } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void CFlexSceneFileManager::DeleteSceneFiles() { @@ -536,8 +536,8 @@ void CFlexSceneFileManager::DeleteSceneFiles() CFlexSceneFileManager g_FlexSceneFileManager; //----------------------------------------------------------------------------- -// Purpose: -// Input : *filename - +// Purpose: +// Input : *filename - //----------------------------------------------------------------------------- void *C_BaseFlex::FindSceneFile( const char *filename ) { @@ -582,7 +582,7 @@ Vector C_BaseFlex::SetViewTarget( CStudioHdr *pStudioHdr ) Vector local; VectorITransform( tmp, attToWorld, local ); - + // FIXME: clamp distance to something based on eyeball distance if (local.x < 6) { @@ -599,7 +599,7 @@ Vector C_BaseFlex::SetViewTarget( CStudioHdr *pStudioHdr ) mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller( m_iEyeUpdown ); eyeAng.x = g_flexweight[ pflex->localToGlobal ]; } - + if ( m_iEyeRightleft != -1 ) { mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller( m_iEyeRightleft ); @@ -644,8 +644,8 @@ Vector C_BaseFlex::SetViewTarget( CStudioHdr *pStudioHdr ) modelrender->SetViewTarget( GetModelPtr(), GetBody(), tmp ); /* - debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%.2f %.2f %.2f : %.2f %.2f %.2f", - m_viewtarget.x, m_viewtarget.y, m_viewtarget.z, + debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%.2f %.2f %.2f : %.2f %.2f %.2f", + m_viewtarget.x, m_viewtarget.y, m_viewtarget.z, m_prevviewtarget.x, m_prevviewtarget.y, m_prevviewtarget.z ); */ @@ -656,14 +656,14 @@ Vector C_BaseFlex::SetViewTarget( CStudioHdr *pStudioHdr ) #define WEAK_CROSSFADE_START 0.40f //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Here's the formula // 0.5 is neutral 100 % of the default setting // Crossfade starts at STRONG_CROSSFADE_START and is full at STRONG_CROSSFADE_END // If there isn't a strong then the intensity of the underlying phoneme is fixed at 2 x STRONG_CROSSFADE_START // so we don't get huge numbers -// Input : *classes - -// emphasis_intensity - +// Input : *classes - +// emphasis_intensity - //----------------------------------------------------------------------------- void C_BaseFlex::ComputeBlendedSetting( Emphasized_Phoneme *classes, float emphasis_intensity ) { @@ -683,7 +683,7 @@ void C_BaseFlex::ComputeBlendedSetting( Emphasized_Phoneme *classes, float empha float frac = dist_remaining / ( 1.0f - STRONG_CROSSFADE_START ); classes[ PHONEME_CLASS_NORMAL ].amount = (frac) * 2.0f * STRONG_CROSSFADE_START; - classes[ PHONEME_CLASS_STRONG ].amount = 1.0f - frac; + classes[ PHONEME_CLASS_STRONG ].amount = 1.0f - frac; } else { @@ -700,7 +700,7 @@ void C_BaseFlex::ComputeBlendedSetting( Emphasized_Phoneme *classes, float empha float frac = dist_remaining / ( WEAK_CROSSFADE_START ); classes[ PHONEME_CLASS_NORMAL ].amount = (1.0f - frac) * 2.0f * WEAK_CROSSFADE_START; - classes[ PHONEME_CLASS_WEAK ].amount = frac; + classes[ PHONEME_CLASS_WEAK ].amount = frac; } else { @@ -716,11 +716,11 @@ void C_BaseFlex::ComputeBlendedSetting( Emphasized_Phoneme *classes, float empha } //----------------------------------------------------------------------------- -// Purpose: -// Input : *classes - -// phoneme - -// scale - -// newexpression - +// Purpose: +// Input : *classes - +// phoneme - +// scale - +// newexpression - //----------------------------------------------------------------------------- void C_BaseFlex::AddViseme( Emphasized_Phoneme *classes, float emphasis_intensity, int phoneme, float scale, bool newexpression ) { @@ -733,7 +733,7 @@ void C_BaseFlex::AddViseme( Emphasized_Phoneme *classes, float emphasis_intensit { return; } - + // Compute blend weights ComputeBlendedSetting( classes, emphasis_intensity ); @@ -772,7 +772,7 @@ void C_BaseFlex::AddViseme( Emphasized_Phoneme *classes, float emphasis_intensit // Purpose: A lot of the one time setup and also resets amount to 0.0f default // for strong/weak/normal tracks // Returning true == skip this phoneme -// Input : *classes - +// Input : *classes - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool C_BaseFlex::SetupEmphasisBlend( Emphasized_Phoneme *classes, int phoneme ) @@ -813,17 +813,17 @@ bool C_BaseFlex::SetupEmphasisBlend( Emphasized_Phoneme *classes, int phoneme ) info->valid = true; } } - + return skip; } //----------------------------------------------------------------------------- -// Purpose: -// Input : *classes - -// *sentence - -// t - -// dt - -// juststarted - +// Purpose: +// Input : *classes - +// *sentence - +// t - +// dt - +// juststarted - //----------------------------------------------------------------------------- ConVar g_CV_PhonemeSnap("phonemesnap", "2", 0, "Lod at level at which visemes stops always considering two phonemes, regardless of duration." ); void C_BaseFlex::AddVisemesForSentence( Emphasized_Phoneme *classes, float emphasis_intensity, CSentence *sentence, float t, float dt, bool juststarted ) @@ -917,8 +917,8 @@ void C_BaseFlex::AddVisemesForSentence( Emphasized_Phoneme *classes, float empha } //----------------------------------------------------------------------------- -// Purpose: -// Input : *classes - +// Purpose: +// Input : *classes - //----------------------------------------------------------------------------- void C_BaseFlex::ProcessVisemes( Emphasized_Phoneme *classes ) { @@ -1075,7 +1075,7 @@ void C_BaseFlex::GetToolRecordingState( KeyValues *msg ) { rightleft = RemapVal( rightleft, flexrightleft->min, flexrightleft->max, 0.0f, 1.0f ); } - + g_flexweight[ flexupdown->localToGlobal ] = updown; g_flexweight[ flexrightleft->localToGlobal ] = rightleft; } @@ -1102,7 +1102,7 @@ void C_BaseFlex::GetToolRecordingState( KeyValues *msg ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void C_BaseFlex::OnThreadedDrawSetup() { @@ -1128,7 +1128,7 @@ bool C_BaseFlex::UsesFlexDelayedWeights() //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void C_BaseFlex::LinkToGlobalFlexControllers( CStudioHdr *hdr ) { @@ -1149,7 +1149,10 @@ void C_BaseFlex::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightC { // hack in an initialization LinkToGlobalFlexControllers( GetModelPtr() ); + +#ifndef MAPBASE m_iBlink = AddGlobalFlexController( "blink" ); +#endif if ( SetupGlobalWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ) ) { @@ -1398,7 +1401,7 @@ const flexsetting_t *C_BaseFlex::FindNamedSetting( const flexsettinghdr_t *pSett } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void C_BaseFlex::StartChoreoScene( CChoreoScene *scene ) { @@ -1412,7 +1415,7 @@ void C_BaseFlex::StartChoreoScene( CChoreoScene *scene ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void C_BaseFlex::RemoveChoreoScene( CChoreoScene *scene ) { @@ -1474,11 +1477,11 @@ bool C_BaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool can //----------------------------------------------------------------------------- // Purpose: Add string indexed scene/expression/duration to list of active SceneEvents -// Input : scenefile - -// expression - -// duration - +// Input : scenefile - +// expression - +// duration - //----------------------------------------------------------------------------- -void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide ) +void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide, C_SceneEntity* pSceneEntity ) { if ( !scene || !event ) { @@ -1503,6 +1506,7 @@ void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseE info.m_hTarget = pTarget; info.m_bStarted = false; info.m_bClientSide = bClientSide; + info.m_hSceneEntity = pSceneEntity; if (StartSceneEvent( &info, scene, event, actor, pTarget )) { @@ -1516,7 +1520,7 @@ void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseE } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool C_BaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ) { @@ -1531,8 +1535,8 @@ bool C_BaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CC case CChoreoEvent::EXPRESSION: return true; - - case CChoreoEvent::SEQUENCE: + + case CChoreoEvent::SEQUENCE: if ( info->m_bClientSide ) { return RequestStartSequenceSceneEvent( info, scene, event, actor, pTarget ); @@ -1551,7 +1555,7 @@ bool C_BaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CC } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool C_BaseFlex::RequestStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ) { @@ -1567,8 +1571,8 @@ bool C_BaseFlex::RequestStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoS //----------------------------------------------------------------------------- // Purpose: Remove expression -// Input : scenefile - -// expression - +// Input : scenefile - +// expression - //----------------------------------------------------------------------------- void C_BaseFlex::RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill ) { @@ -1619,7 +1623,7 @@ bool C_BaseFlex::CheckSceneEvent( float currenttime, CChoreoScene *scene, CChore if ( info->m_pEvent != event) continue; - + return CheckSceneEventCompletion( info, currenttime, scene, event ); } return true; @@ -1666,7 +1670,7 @@ float C_BaseFlex::GetFlexWeight( LocalFlexController_t index ) { return m_flexWeight[index] * (pflexcontroller->max - pflexcontroller->min) + pflexcontroller->min; } - + return m_flexWeight[index]; } return 0.0; @@ -1728,7 +1732,7 @@ void C_BaseFlex::ProcessSceneEvents( bool bFlexEvents ) } //----------------------------------------------------------------------------- -// Various methods to process facial SceneEvents: +// Various methods to process facial SceneEvents: //----------------------------------------------------------------------------- bool C_BaseFlex::ProcessFlexAnimationSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ) { @@ -1751,7 +1755,7 @@ bool C_BaseFlex::ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoSce // Look up the actual strings const char *scenefile = event->GetParameters(); const char *name = event->GetParameters2(); - + // Have to find both strings if ( scenefile && name ) { @@ -1760,9 +1764,9 @@ bool C_BaseFlex::ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoSce if ( pExpHdr ) { float scenetime = scene->GetTime(); - + float scale = event->GetIntensity( scenetime ); - + // Add the named expression AddFlexSetting( name, scale, pExpHdr, !info->m_bStarted ); } @@ -1774,8 +1778,8 @@ bool C_BaseFlex::ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoSce //----------------------------------------------------------------------------- // Purpose: Each CBaseFlex maintains a UtlRBTree of mappings, one for each loaded flex scene file it uses. This is used to // sort the entries in the RBTree -// Input : lhs - -// rhs - +// Input : lhs - +// rhs - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool FlexSettingLessFunc( const FS_LocalToGlobal_t& lhs, const FS_LocalToGlobal_t& rhs ) @@ -1784,9 +1788,9 @@ bool FlexSettingLessFunc( const FS_LocalToGlobal_t& lhs, const FS_LocalToGlobal_ } //----------------------------------------------------------------------------- -// Purpose: Since everyone shared a pSettinghdr now, we need to set up the localtoglobal mapping per entity, but +// Purpose: Since everyone shared a pSettinghdr now, we need to set up the localtoglobal mapping per entity, but // we just do this in memory with an array of integers (could be shorts, I suppose) -// Input : *pSettinghdr - +// Input : *pSettinghdr - //----------------------------------------------------------------------------- void C_BaseFlex::EnsureTranslations( const flexsettinghdr_t *pSettinghdr ) { @@ -1810,8 +1814,8 @@ void C_BaseFlex::EnsureTranslations( const flexsettinghdr_t *pSettinghdr ) //----------------------------------------------------------------------------- // Purpose: Look up instance specific mapping -// Input : *pSettinghdr - -// key - +// Input : *pSettinghdr - +// key - // Output : int //----------------------------------------------------------------------------- int C_BaseFlex::FlexControllerLocalToGlobal( const flexsettinghdr_t *pSettinghdr, int key ) @@ -1840,13 +1844,13 @@ int C_BaseFlex::FlexControllerLocalToGlobal( const flexsettinghdr_t *pSettinghdr } //----------------------------------------------------------------------------- -// Purpose: -// Input : *expr - -// scale - -// *pSettinghdr - -// newexpression - +// Purpose: +// Input : *expr - +// scale - +// *pSettinghdr - +// newexpression - //----------------------------------------------------------------------------- -void C_BaseFlex::AddFlexSetting( const char *expr, float scale, +void C_BaseFlex::AddFlexSetting( const char *expr, float scale, const flexsettinghdr_t *pSettinghdr, bool newexpression ) { int i; @@ -1888,7 +1892,7 @@ void C_BaseFlex::AddFlexSetting( const char *expr, float scale, } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool C_BaseFlex::ProcessSceneEvent( bool bFlexEvents, CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ) { @@ -1933,9 +1937,9 @@ bool C_BaseFlex::ProcessSceneEvent( bool bFlexEvents, CSceneEventInfo *info, CCh } //----------------------------------------------------------------------------- -// Purpose: -// Input : *actor - -// *parameters - +// Purpose: +// Input : *actor - +// *parameters - //----------------------------------------------------------------------------- bool C_BaseFlex::ProcessSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ) { @@ -1947,11 +1951,11 @@ bool C_BaseFlex::ProcessSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene } //----------------------------------------------------------------------------- -// Purpose: -// Input : *event - +// Purpose: +// Input : *event - //----------------------------------------------------------------------------- void C_BaseFlex::AddFlexAnimation( CSceneEventInfo *info ) -{ +{ if ( !info ) return; diff --git a/game/client/c_baseflex.h b/game/client/c_baseflex.h index 56c86cb32..ab8029858 100644 --- a/game/client/c_baseflex.h +++ b/game/client/c_baseflex.h @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -61,7 +61,7 @@ struct FS_LocalToGlobal_t m_nCount( 0 ), m_Mapping( 0 ) { - } + } void SetCount( int count ) { @@ -103,7 +103,7 @@ class IHasLocalToGlobalFlexSettings }; //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- struct Emphasized_Phoneme { @@ -121,7 +121,7 @@ struct Emphasized_Phoneme }; //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- class C_BaseFlex : public C_BaseAnimatingOverlay, public IHasLocalToGlobalFlexSettings { @@ -214,7 +214,7 @@ class C_BaseFlex : public C_BaseAnimatingOverlay, public IHasLocalToGlobalFlexSe virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); // Add the event to the queue for this actor - void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false ); + void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false, C_SceneEntity* pSceneEntity = NULL ); // Remove the event from the queue for this actor void RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill ); @@ -239,7 +239,7 @@ class C_BaseFlex : public C_BaseAnimatingOverlay, public IHasLocalToGlobalFlexSe bool ProcessFlexAnimationSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ); bool ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ); - void AddFlexSetting( const char *expr, float scale, + void AddFlexSetting( const char *expr, float scale, const flexsettinghdr_t *pSettinghdr, bool newexpression ); // Array of active SceneEvents, in order oldest to newest diff --git a/game/client/c_baselesson.cpp b/game/client/c_baselesson.cpp new file mode 100644 index 000000000..7124d9a49 --- /dev/null +++ b/game/client/c_baselesson.cpp @@ -0,0 +1,3895 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler implementations for instructing players how to play +// +//=====================================================================================// + +#include "cbase.h" + +#include "c_baselesson.h" +#include "c_gameinstructor.h" + +#include "hud_locator_target.h" +#include "c_world.h" +#include "iinput.h" +#include "ammodef.h" +#include "vprof.h" +#include "view.h" +#include "vstdlib/IKeyValuesSystem.h" +#ifdef MAPBASE +#include "usermessages.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Configuración +//========================================================= + +#define LESSON_PRIORITY_MAX 1000 +#define LESSON_PRIORITY_NONE 0 +#define LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED 1.5f +#define LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED 0.1f +#define LESSON_DISTANCE_UPDATE_RATE 0.25f + +// See comments in UtlSymbol on why this is useful and how it works +IMPLEMENT_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol ); + +extern ConVar gameinstructor_verbose; +extern ConVar gameinstructor_verbose_lesson; +extern ConVar gameinstructor_find_errors; + +#ifdef MAPBASE +// Mapbase was originally going to use a HL2-style default color (245,232,179). +// This is no longer the case, but mods are free to change this cvar in their config files. +ConVar gameinstructor_default_captioncolor( "gameinstructor_default_captioncolor", "255,255,255", FCVAR_NONE ); +ConVar gameinstructor_default_bindingcolor( "gameinstructor_default_bindingcolor", "0,0,0", FCVAR_NONE ); +#endif // MAPBASE + +// +// CGameInstructorLesson +// + +Color CBaseLesson::m_rgbaVerboseHeader = Color( 255, 128, 64, 255 ); +Color CBaseLesson::m_rgbaVerbosePlain = Color( 64, 128, 255, 255 ); +Color CBaseLesson::m_rgbaVerboseName = Color( 255, 255, 255, 255 ); +Color CBaseLesson::m_rgbaVerboseOpen = Color( 0, 255, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseClose = Color( 255, 0, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseSuccess = Color( 255, 255, 0, 255 ); +Color CBaseLesson::m_rgbaVerboseUpdate = Color( 255, 0, 255, 255 ); + + +//========================================================= +// Constructor +//========================================================= +CBaseLesson::CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity ) +{ + COMPILE_TIME_ASSERT( sizeof( CGameInstructorSymbol ) == sizeof( CUtlSymbol ) ); + + m_stringName = pchName; + m_stringReplaceKey = ""; + m_bIsDefaultHolder = bIsDefaultHolder; + m_bIsOpenOpportunity = bIsOpenOpportunity; + + Init(); +} + +//========================================================= +// Destructor +//========================================================= +CBaseLesson::~CBaseLesson() +{ + // Remove from root's children list + if ( m_pRoot ) + m_pRoot->m_OpenOpportunities.FindAndRemove(this); + + else + { + for ( int i = 0; i < m_OpenOpportunities.Count(); ++i ) + { + // Remove from children if they are still around + CBaseLesson *pLesson = m_OpenOpportunities[ i ]; + pLesson->m_pRoot = NULL; + } + } +} + +//========================================================= +//========================================================= +void CBaseLesson::AddPrerequisite( const char *pchLessonName ) +{ + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "\t%s: ", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Adding prereq " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pchLessonName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + + const CBaseLesson *pPrerequisite = GetGameInstructor().GetLesson( pchLessonName ); + + if ( !pPrerequisite ) + { + DevWarning( "Prerequisite %s added by lesson %s doesn't exist!\n", pchLessonName, GetName() ); + return; + } + + m_Prerequisites.AddToTail(pPrerequisite); +} + +//========================================================= +//========================================================= +void CBaseLesson::SetRoot( CBaseLesson *pRoot ) +{ + m_pRoot = pRoot; + + if ( m_pRoot->m_OpenOpportunities.Find( this ) == -1 ) + m_pRoot->m_OpenOpportunities.AddToTail( this ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::ShouldShowSpew() +{ + // @DEBUG + return true; + + if ( gameinstructor_verbose_lesson.GetString()[ 0 ] == '\0' ) + return false; + + return ( Q_stristr( GetName(), gameinstructor_verbose_lesson.GetString() ) != NULL ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::NoPriority() const +{ + return ( m_iPriority == LESSON_PRIORITY_NONE ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsLocked() const +{ + if ( m_fLockDuration == 0.0f ) + return false; + + if ( !IsInstructing() || !IsVisible() ) + return false; + + float fLockTime = m_fLockTime; + + if ( fLockTime == 0.0f ) + fLockTime = m_fStartTime; + + return ( gpGlobals->curtime > m_fStartTime + LESSON_MIN_TIME_BEFORE_LOCK_ALLOWED && gpGlobals->curtime < fLockTime + m_fLockDuration ); +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsLearned() const +{ + if ( m_iDisplayLimit > 0 && m_iDisplayCount >= m_iDisplayLimit ) + return true; + + if ( m_iSuccessLimit > 0 && m_iSuccessCount >= m_iSuccessLimit ) + return true; + + return false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::PrerequisitesHaveBeenMet() const +{ + for ( int i = 0; i < m_Prerequisites.Count(); ++i ) + { + if ( !m_Prerequisites[ i ]->IsLearned() ) + { + // Failed a prereq + return false; + } + } + + // All prereqs passed + return true; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IsTimedOut() +{ + VPROF_BUDGET( "CBaseLesson::IsTimedOut", "GameInstructor" ); + + // Check for no timeout + if ( m_fTimeout == 0.0f ) + return false; + + float fStartTime = m_fStartTime; + + if ( GetRoot()->IsLearned() ) + { + if ( !m_bBumpWithTimeoutWhenLearned ) + { + // Time out instantly if we've learned this and don't want to keep it open for priority bumping + return true; + } + else + { + // It'll never be active, so lets use timeout based on when it was initialized + fStartTime = m_fInitTime; + } + } + + if ( !fStartTime ) + { + if ( !m_bCanTimeoutWhileInactive ) + { + return false; + } + + // Not active, so lets use timeout based on when it was initialized + fStartTime = m_fInitTime; + } + + bool bTimedOut = ( fStartTime + m_fTimeout < gpGlobals->curtime ); + + if ( bTimedOut ) + SetCloseReason( "Timed out." ); + + return bTimedOut; +} + +//========================================================= +//========================================================= +void CBaseLesson::ResetDisplaysAndSuccesses() +{ + m_iDisplayCount = 0; + m_bSuccessCounted = false; + m_iSuccessCount = 0; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IncDisplayCount() +{ + if ( m_iDisplayCount < m_iDisplayLimit ) + { + m_iDisplayCount++; + return true; + } + + return false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::IncSuccessCount() +{ + if ( m_iSuccessCount < m_iSuccessLimit ) + { + m_iSuccessCount++; + return true; + } + + return false; +} + +//========================================================= +//========================================================= +void CBaseLesson::Init() +{ + m_pRoot = NULL; + m_bSuccessCounted = false; + + SetCloseReason( "None given." ); + + m_iPriority = LESSON_PRIORITY_MAX; // Set to invalid value to ensure that it is actually set later on + m_iInstanceType = LESSON_INSTANCE_MULTIPLE; + m_iFixedInstancesMax = 1; + m_bReplaceOnlyWhenStopped = false; + m_iTeam = TEAM_ANY; + m_bOnlyKeyboard = false; + m_bOnlyGamepad = false; + + m_iDisplayLimit = 0; + m_iDisplayCount = 0; + m_bWasDisplayed = false; + + m_iSuccessLimit = 0; + m_iSuccessCount = 0; + + m_fLockDuration = 0.0f; + m_bCanOpenWhenDead = false; + m_bBumpWithTimeoutWhenLearned = false; + m_bCanTimeoutWhileInactive = false; + m_fTimeout = 0.0f; + + m_fInitTime = gpGlobals->curtime; + m_fStartTime = 0.0f; + m_fLockTime = 0.0f; + + m_fUpdateInterval = 0.5; + m_bHasPlayedSound = false; + + m_szStartSound = "Instructor.LessonStart"; + m_szLessonGroup = ""; + + m_iNumDelayedPlayerSwaps = 0; +} + +//========================================================= +//========================================================= +void CBaseLesson::TakePlaceOf( CBaseLesson *pLesson ) +{ + // Transfer over marked as displayed so a replaced lesson won't count as an extra display + m_bWasDisplayed = pLesson->m_bWasDisplayed; + pLesson->m_bWasDisplayed = false; +} + +//========================================================= +//========================================================= +void CBaseLesson::MarkSucceeded() +{ + if ( !m_bSuccessCounted ) + { + GetGameInstructor().MarkSucceeded( GetName() ); + m_bSuccessCounted = true; + } +} + +//========================================================= +//========================================================= +void CBaseLesson::CloseOpportunity( const char *pchReason ) +{ + SetCloseReason(pchReason); + m_bIsOpenOpportunity = false; +} + +//========================================================= +//========================================================= +bool CBaseLesson::DoDelayedPlayerSwaps() const +{ + // A bot has swapped places with a player or player with a bot... + // At the time of the actual swap there was no client representation for the new player... + // So that swap was queued up and now we're going to make things right! + while ( m_iNumDelayedPlayerSwaps ) + { + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].iNewUserID ); + + if ( !pNewPlayer ) + { + // There is still no client representation of the new player, we'll have to try again later + if ( gameinstructor_verbose.GetInt() > 1 ) + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tFailed delayed player swap!" ); + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 1 ) + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tSuccessful delayed player swap!" ); + + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps - 1 ].phHandleToChange->Set( pNewPlayer ); + m_iNumDelayedPlayerSwaps--; + } + + return true; +} + + +// +// CTextLesson +// + +//========================================================= +//========================================================= +void CTextLesson::Init() +{ + m_szDisplayText = ""; + m_szDisplayParamText = ""; + m_szBinding = ""; + m_szGamepadBinding = ""; +} + +//========================================================= +//========================================================= +void CTextLesson::Start() +{ + // TODO: Display some text + //m_szDisplayText +} + +//========================================================= +//========================================================= +void CTextLesson::Stop() +{ + // TODO: Clean up text +} + +// +// CIconLesson +// + +void CIconLesson::Init() +{ + m_hIconTarget = NULL; + m_szVguiTargetName = ""; + m_szVguiTargetLookup = ""; + m_nVguiTargetEdge = 0; + + m_hLocatorTarget = -1; + m_bFixedPosition = false; + m_bNoIconTarget = false; + m_bAllowNodrawTarget = false; + + m_bVisible = true; + m_bShowWhenOccluded = true; + m_bNoOffscreen = false; + m_bForceCaption = false; + + m_szOnscreenIcon = ""; + m_szOffscreenIcon = ""; + + m_flUpOffset = 0.0f; + m_flRelativeUpOffset = 0.0f; + m_fFixedPositionX = 0.0f; + m_fFixedPositionY = 0.0f; + + m_fRange = 0.0f; + m_fCurrentDistance = 0.0f; + + m_fOnScreenStartTime = 0.0f; + m_fUpdateDistanceTime = 0.0f; + + m_iFlags = LOCATOR_ICON_FX_NONE; +#ifdef MAPBASE + m_szCaptionColor = gameinstructor_default_captioncolor.GetString(); + + m_iIconTargetPos = ICON_TARGET_EYE_POSITION; + m_szHudHint = ""; +#else + m_szCaptionColor = "255,255,255";// Default to white +#endif // MAPBASE +} + +//========================================================= +//========================================================= +void CIconLesson::Start() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + // Display some text + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + // Wanted one, but couldn't get it + CloseOpportunity( "Icon Target handle went invalid before the lesson started!" ); + } + + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + CLocatorTarget *pLocatorTarget = NULL; + + if ( m_hLocatorTarget != -1 ) + { + // Lets try the handle that we've held on to + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + { + // It's gone stale, get a new target + m_hLocatorTarget = Locator_AddTarget(); + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + } + } + else + { + // Get a new target + m_hLocatorTarget = Locator_AddTarget(); + pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + } + + if( m_hLocatorTarget == -1 || !pLocatorTarget ) + { + CloseOpportunity( "Could not get a handle for new locator target. Too many targets in use!" ); + return; + } + + pLocatorTarget->AddIconEffects( m_iFlags ); + pLocatorTarget->SetCaptionColor( GetCaptionColorString() ); + UpdateLocatorTarget( pLocatorTarget, pIconTarget ); + + // Update occlusion data + Locator_ComputeTargetIconPositionFromHandle( m_hLocatorTarget ); +} + +//========================================================= +//========================================================= +void CIconLesson::Stop() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + if ( m_hLocatorTarget != -1 ) + Locator_RemoveTarget( m_hLocatorTarget ); + + m_fOnScreenStartTime = 0.0f; +} + +//========================================================= +//========================================================= +void CIconLesson::Update() +{ + if ( !DoDelayedPlayerSwaps() ) + return; + + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + CloseOpportunity( "Lost our icon target handle returned NULL." ); + } + + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + if ( !pLocatorTarget ) + { + // Temp instrumentation to catch a bug - possibly calling Update without having called Start? + Warning( "Problem in lesson %s: Locator_GetTargetFromHandle returned null for handle %d.\n IsInstanceActive: %s. IsInstructing: %s. IsLearned: %s\n", + GetName(), m_hLocatorTarget, + (IsInstanceActive() ? "yes" : "no"), + (IsInstructing() ? "yes" : "no"), + (IsLearned() ? "yes" : "no") ); + CloseOpportunity( "Lost locator target handle." ); + return; + } + + UpdateLocatorTarget( pLocatorTarget, pIconTarget ); + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + // Check if it has been onscreen long enough to count as being displayed + if ( m_fOnScreenStartTime == 0.0f ) + { + if ( pLocatorTarget->IsOnScreen() && ( IsPresentComplete() || ( pLocatorTarget->GetIconEffectsFlags() & LOCATOR_ICON_FX_STATIC ) ) ) + { + // Is either static or has finished presenting and is on screen + m_fOnScreenStartTime = gpGlobals->curtime; + } + } + else + { + if ( !pLocatorTarget->IsOnScreen() ) + { + // Was visible before, but it isn't now + m_fOnScreenStartTime = 0.0f; + } + else if ( gpGlobals->curtime - m_fOnScreenStartTime >= LESSON_MIN_TIME_ON_SCREEN_TO_MARK_DISPLAYED ) + { + // Lesson on screen long enough to be counted as displayed + m_bWasDisplayed = true; + } + } + + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + // Update it's distance from the local player + C_BaseEntity *pTarget = m_hIconTarget.Get(); + + if ( !pLocalPlayer || !pTarget || pLocalPlayer == pTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pTarget->WorldSpaceCenter() ); + } + + m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE; + } +} + +void CIconLesson::UpdateInactive() +{ + if ( m_fUpdateDistanceTime < gpGlobals->curtime ) + { + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + C_BaseEntity *pIconTarget = m_hIconTarget.Get(); + + if ( !pIconTarget ) + { + if ( !m_bNoIconTarget ) + { + CloseOpportunity( "Lost our icon target handle returned NULL." ); + } + + m_fCurrentDistance = 0.0f; + return; + } + else + { + if ( ( pIconTarget->IsEffectActive( EF_NODRAW ) || pIconTarget->IsDormant() ) && !m_bAllowNodrawTarget ) + { + // We don't allow no draw entities + CloseOpportunity( "Icon Target is using effect NODRAW and allow_nodraw_target is false!" ); + return; + } + } + + // Update it's distance from the local player + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( !pLocalPlayer || pLocalPlayer == pIconTarget ) + { + m_fCurrentDistance = 0.0f; + } + else + { + m_fCurrentDistance = pLocalPlayer->EyePosition().DistTo( pIconTarget->WorldSpaceCenter() ); + } + +#ifdef MAPBASE + if (m_szHudHint.String()[0] != '\0' && GetRoot()->IsLearned()) + { + DevMsg("Showing hint\n"); + CUtlBuffer msg_data; + msg_data.PutChar( 1 ); + msg_data.PutString( m_szHudHint.String() ); + bf_read msg( msg_data.Base(), msg_data.TellPut() ); + usermessages->DispatchUserMessage( usermessages->LookupUserMessage( "KeyHintText" ), msg ); + } +#endif // MAPBASE + + m_fUpdateDistanceTime = gpGlobals->curtime + LESSON_DISTANCE_UPDATE_RATE; + } +} + +bool CIconLesson::ShouldDisplay() const +{ + VPROF_BUDGET( "CIconLesson::ShouldDisplay", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return false; + } + + if ( m_fRange > 0.0f && m_fCurrentDistance > m_fRange ) + { + // Distance to target is more than the max range + return false; + } + + if ( !m_bShowWhenOccluded && m_hLocatorTarget >= 0 ) + { + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( pLocatorTarget && pLocatorTarget->IsOccluded() ) + { + // Target is occluded and doesn't want to be shown when occluded + return false; + } + } + + // Ok to display + return true; +} + +bool CIconLesson::IsVisible() const +{ + VPROF_BUDGET( "CIconLesson::IsVisible", "GameInstructor" ); + + if( m_hLocatorTarget == -1 ) + { + // If it doesn't want a target, it's "visible" otherwise we'll have to call it invisible + return m_bNoIconTarget; + } + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + if ( !pLocatorTarget ) + { + return false; + } + + return pLocatorTarget->IsVisible(); +} + +void CIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID ) +{ + BaseClass::SwapOutPlayers( iOldUserID, iNewUserID ); + + if ( m_bNoIconTarget ) + return; + + // Get the player pointers from the user IDs + C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID ); + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID ); + + if ( pOldPlayer == m_hIconTarget.Get() ) + { + if ( pNewPlayer ) + { + m_hIconTarget = pNewPlayer; + } + else + { + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hIconTarget; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } +} + +void CIconLesson::TakePlaceOf( CBaseLesson *pLesson ) +{ + BaseClass::TakePlaceOf( pLesson ); + + const CIconLesson *pIconLesson = dynamic_cast( pLesson ); + + if ( pIconLesson ) + { + if ( pIconLesson->m_hLocatorTarget != -1 ) + { + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( pIconLesson->m_hLocatorTarget ); + + if ( pLocatorTarget ) + { + // This one draw right to the hud... use it's icon target handle + m_hLocatorTarget = pIconLesson->m_hLocatorTarget; + } + } + + m_fOnScreenStartTime = pIconLesson->m_fOnScreenStartTime; + } +} + +void CIconLesson::SetLocatorBinding( CLocatorTarget * pLocatorTarget ) +{ + if ( IsX360() /*|| input->ControllerModeActive()*/ ) + { + // Try to use gamepad bindings first + if ( m_szGamepadBinding.String()[ 0 ] != '\0' ) + { + // Found gamepad binds! + pLocatorTarget->SetBinding( m_szGamepadBinding.String() ); + } + else + { + // No gamepad binding, so fallback to the regular binding + pLocatorTarget->SetBinding( m_szBinding.String() ); + } + } + else + { + // Always use the regular binding when the gamepad is disabled + pLocatorTarget->SetBinding( m_szBinding.String() ); + } +} + +bool CIconLesson::IsPresentComplete() +{ + if ( m_hLocatorTarget == -1 ) + return false; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return false; + + return !pLocatorTarget->IsPresenting(); +} + +void CIconLesson::PresentStart() +{ + if ( m_hLocatorTarget == -1 ) + return; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return; + + pLocatorTarget->StartPresent(); +} + +void CIconLesson::PresentEnd() +{ + if ( m_hLocatorTarget == -1 ) + return; + + CLocatorTarget *pLocatorTarget = Locator_GetTargetFromHandle( m_hLocatorTarget ); + + if ( !pLocatorTarget ) + return; + + pLocatorTarget->EndPresent(); +} + +void CIconLesson::UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget ) +{ + if ( m_bFixedPosition ) + { + pLocatorTarget->m_bOriginInScreenspace = true; + pLocatorTarget->m_vecOrigin.x = m_fFixedPositionX; + pLocatorTarget->m_vecOrigin.y = m_fFixedPositionY; + pLocatorTarget->SetVguiTargetName( m_szVguiTargetName.String() ); + pLocatorTarget->SetVguiTargetLookup( m_szVguiTargetLookup.String() ); + pLocatorTarget->SetVguiTargetEdge( m_nVguiTargetEdge ); + } + else + { + pLocatorTarget->m_bOriginInScreenspace = false; +#ifdef MAPBASE + pLocatorTarget->m_vecOrigin = GetIconTargetPosition( pIconTarget ) + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset ); +#else + pLocatorTarget->m_vecOrigin = pIconTarget->EyePosition() + MainViewUp() * m_flRelativeUpOffset + Vector( 0.0f, 0.0f, m_flUpOffset ); +#endif // MAPBASE + pLocatorTarget->SetVguiTargetName( "" ); + } + + const char *pchDisplayParamText = m_szDisplayParamText.String(); +#ifdef INFESTED_DLL + char szCustomName[ 256 ]; +#endif // INFESTED_DLL + + // Check if the parameter is the be the player display name + if ( Q_stricmp( pchDisplayParamText, "use_name" ) == 0 ) + { + // Fix up the player display name + C_BasePlayer *pPlayer = ToBasePlayer( pIconTarget ); + if ( pPlayer ) + { + pchDisplayParamText = pPlayer->GetPlayerName(); + } + else + { + bool bNoName = true; + +#ifdef INFESTED_DLL + C_ASW_Marine *pMarine = dynamic_cast< C_ASW_Marine* >( pIconTarget ); + if ( pMarine ) + { + C_ASW_Marine_Resource *pMR = pMarine->GetMarineResource(); + if ( pMR ) + { + pMR->GetDisplayName( szCustomName, sizeof( szCustomName ) ); + pchDisplayParamText = szCustomName; + bNoName = false; + } + } +#endif // INFESTED_DLL + + if ( bNoName ) + { + // It's not a player! + pchDisplayParamText = ""; + } + } + } + + pLocatorTarget->SetCaptionText( m_szDisplayText.String(), pchDisplayParamText ); + SetLocatorBinding( pLocatorTarget ); + pLocatorTarget->SetOnscreenIconTextureName( m_szOnscreenIcon.String() ); + pLocatorTarget->SetOffscreenIconTextureName( m_szOffscreenIcon.String() ); + pLocatorTarget->SetVisible( m_bVisible ); + + C_BasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if( !m_bFixedPosition && + ( ( pLocalPlayer != NULL && pLocalPlayer == m_hIconTarget ) || + GetClientWorldEntity() == m_hIconTarget ) ) + { + // Mark this icon as a static icon that draws in a fixed + // location on the hud rather than tracking an object + // in 3D space. + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_STATIC ); + } + else + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NONE ); + } + + if ( m_bNoOffscreen ) + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN ); + } + else + { + pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_NO_OFFSCREEN ); + } + + if( m_bForceCaption || IsLocked() ) + { + pLocatorTarget->AddIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION ); + } + else + { + pLocatorTarget->RemoveIconEffects( LOCATOR_ICON_FX_FORCE_CAPTION ); + } + + pLocatorTarget->Update(); + + if ( pLocatorTarget->m_bIsDrawing ) + { + if ( !m_bHasPlayedSound ) + { + GetGameInstructor().PlaySound( m_szStartSound.String() ); + m_bHasPlayedSound = true; + } + } +} + +#ifdef MAPBASE +Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget ) +{ + switch (m_iIconTargetPos) + { + default: + case ICON_TARGET_EYE_POSITION: + return pIconTarget->EyePosition(); + + case ICON_TARGET_ORIGIN: + return pIconTarget->GetAbsOrigin(); + + case ICON_TARGET_CENTER: + return pIconTarget->WorldSpaceCenter(); + } +} +#endif // MAPBASE + +// +// CScriptedIconLesson +// + +// Linking variables to scriptable entries is done here! +// The first parameter correlates to the case insensitive string name read from scripts. +// This macro generates code that passes this consistent variable data in to other macros +#define LESSON_VARIABLE_FACTORY \ + LESSON_VARIABLE_MACRO_EHANDLE( VOID, m_hLocalPlayer, EHANDLE ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( LOCAL_PLAYER, m_hLocalPlayer, EHANDLE ) \ + LESSON_VARIABLE_MACRO( OUTPUT, m_fOutput, float ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( ENTITY1, m_hEntity1, EHANDLE ) \ + LESSON_VARIABLE_MACRO_EHANDLE( ENTITY2, m_hEntity2, EHANDLE ) \ + LESSON_VARIABLE_MACRO_STRING( STRING1, m_szString1, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( STRING2, m_szString2, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( INTEGER1, m_iInteger1, int ) \ + LESSON_VARIABLE_MACRO( INTEGER2, m_iInteger2, int ) \ + LESSON_VARIABLE_MACRO( FLOAT1, m_fFloat1, float ) \ + LESSON_VARIABLE_MACRO( FLOAT2, m_fFloat2, float ) \ + \ + LESSON_VARIABLE_MACRO_EHANDLE( ICON_TARGET, m_hIconTarget, EHANDLE ) \ + LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_NAME, m_szVguiTargetName, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( VGUI_TARGET_LOOKUP, m_szVguiTargetLookup, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( VGUI_TARGET_EDGE, m_nVguiTargetEdge, int ) \ + LESSON_VARIABLE_MACRO( FIXED_POSITION_X, m_fFixedPositionX, float ) \ + LESSON_VARIABLE_MACRO( FIXED_POSITION_Y, m_fFixedPositionY, float ) \ + LESSON_VARIABLE_MACRO_BOOL( FIXED_POSITION, m_bFixedPosition, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( NO_ICON_TARGET, m_bNoIconTarget, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( ALLOW_NODRAW_TARGET, m_bAllowNodrawTarget, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( VISIBLE, m_bVisible, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( SHOW_WHEN_OCCLUDED, m_bShowWhenOccluded, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( NO_OFFSCREEN, m_bNoOffscreen, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( FORCE_CAPTION, m_bForceCaption, bool ) \ + LESSON_VARIABLE_MACRO_STRING( ONSCREEN_ICON, m_szOnscreenIcon, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( OFFSCREEN_ICON, m_szOffscreenIcon, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO( ICON_OFFSET, m_flUpOffset, float ) \ + LESSON_VARIABLE_MACRO( ICON_RELATIVE_OFFSET, m_flRelativeUpOffset, float ) \ + LESSON_VARIABLE_MACRO( RANGE, m_fRange, float ) \ + \ + LESSON_VARIABLE_MACRO( FLAGS, m_iFlags, int ) \ + LESSON_VARIABLE_MACRO_STRING( CAPTION_COLOR, m_szCaptionColor, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( GROUP, m_szLessonGroup, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO_STRING( CAPTION, m_szDisplayText, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( CAPTION_PARAM, m_szDisplayParamText, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( BINDING, m_szBinding, CGameInstructorSymbol ) \ + LESSON_VARIABLE_MACRO_STRING( GAMEPAD_BINDING, m_szGamepadBinding, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( PRIORITY, m_iPriority, int ) \ + LESSON_VARIABLE_MACRO_STRING( REPLACE_KEY, m_stringReplaceKey, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( LOCK_DURATION, m_fLockDuration, float ) \ + LESSON_VARIABLE_MACRO_BOOL( CAN_OPEN_WHEN_DEAD, m_bCanOpenWhenDead, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( BUMP_WITH_TIMEOUT_WHEN_LEARNED, m_bBumpWithTimeoutWhenLearned, bool ) \ + LESSON_VARIABLE_MACRO_BOOL( CAN_TIMEOUT_WHILE_INACTIVE, m_bCanTimeoutWhileInactive, bool ) \ + LESSON_VARIABLE_MACRO( TIMEOUT, m_fTimeout, float ) \ + LESSON_VARIABLE_MACRO( UPDATE_INTERVAL, m_fUpdateInterval, float ) \ + LESSON_VARIABLE_MACRO_STRING( START_SOUND, m_szStartSound, CGameInstructorSymbol ) \ + \ + LESSON_VARIABLE_MACRO( ICON_TARGET_POS, m_iIconTargetPos, int ) \ + LESSON_VARIABLE_MACRO_STRING( HUD_HINT_AFTER_LEARNED, m_szHudHint, CGameInstructorSymbol ) \ + + +// Create keyvalues name symbol +#define LESSON_VARIABLE_SYMBOL( _varEnum, _varName, _varType ) static int g_n##_varEnum##Symbol; + +#define LESSON_VARIABLE_INIT_SYMBOL( _varEnum, _varName, _varType ) g_n##_varEnum##Symbol = KeyValuesSystem()->GetSymbolForString( #_varEnum ); + +#define LESSON_SCRIPT_STRING_ADD_TO_MAP( _varEnum, _varName, _varType ) g_NameToTypeMap.Insert( #_varEnum, LESSON_VARIABLE_##_varEnum ); + +// Create enum value +#define LESSON_VARIABLE_ENUM( _varEnum, _varName, _varType ) LESSON_VARIABLE_##_varEnum, + +// Init info call +#define LESSON_VARIABLE_INIT_INFO_CALL( _varEnum, _varName, _varType ) g_pLessonVariableInfo[ LESSON_VARIABLE_##_varEnum ].Init_##_varEnum(); + +// Init info +#define LESSON_VARIABLE_INIT_INFO( _varEnum, _varName, _varType ) \ + void Init_##_varEnum() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ + varType = LessonParamTypeFromString( #_varType ); \ + } + +#define LESSON_VARIABLE_INIT_INFO_BOOL( _varEnum, _varName, _varType ) \ + void Init_##_varEnum() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ + varType = FIELD_BOOLEAN; \ + } + +#define LESSON_VARIABLE_INIT_INFO_EHANDLE( _varEnum, _varName, _varType ) \ + void Init_##_varEnum() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ + varType = FIELD_EHANDLE; \ + } + +#define LESSON_VARIABLE_INIT_INFO_STRING( _varEnum, _varName, _varType ) \ + void Init_##_varEnum() \ + { \ + iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \ + varType = FIELD_STRING; \ + } + +// Copy defaults into this scripted lesson into a new one +#define LESSON_VARIABLE_DEFAULT( _varEnum, _varName, _varType ) ( _varName = m_pDefaultHolder->_varName ); + +// Copy a variable from this scripted lesson into a new one +#define LESSON_VARIABLE_COPY( _varEnum, _varName, _varType ) ( pOpenLesson->_varName = _varName ); + +// Return the first param if pchName is the same as the second param +#define LESSON_SCRIPT_STRING( _type, _string ) \ + if ( Q_stricmp( pchName, _string ) == 0 )\ + {\ + return _type;\ + } + +// Wrapper for using this macro in the factory +#define LESSON_SCRIPT_STRING_GENERAL( _varEnum, _varName, _varType ) LESSON_SCRIPT_STRING( LESSON_VARIABLE_##_varEnum##, #_varEnum ) + +// Process the element action on this variable +#define PROCESS_LESSON_ACTION( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float ); + +#define PROCESS_LESSON_ACTION_EHANDLE( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float, eventParam_BaseEntity, eventParam_string ); + +#define PROCESS_LESSON_ACTION_STRING( _varEnum, _varName, _varType ) \ + case LESSON_VARIABLE_##_varEnum:\ + return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, &_varName, &pLessonElement->szParam, eventParam_string ); + +// Init the variable from the script (or a convar) +#define LESSON_VARIABLE_INIT( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + const char *pchParam = pSubKey->GetString(); \ + if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \ + { \ + ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \ + if ( tempCVar.IsValid() ) \ + { \ + _varName = static_cast<_varType>( tempCVar.GetFloat() ); \ + } \ + else \ + { \ + _varName = static_cast<_varType>( 0.0f ); \ + } \ + } \ + else \ + { \ + _varName = static_cast<_varType>( pSubKey->GetFloat() ); \ + } \ + } + +#define LESSON_VARIABLE_INIT_BOOL( _varEnum, _varName, _varType ) \ + else if ( Q_stricmp( #_varEnum, pSubKey->GetName() ) == 0 ) \ + { \ + _varName = pSubKey->GetBool(); \ + } + +#define LESSON_VARIABLE_INIT_EHANDLE( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + DevWarning( "Can't initialize an EHANDLE from the instructor lesson script." ); \ + } + +#define LESSON_VARIABLE_INIT_STRING( _varEnum, _varName, _varType ) \ + else if ( g_n##_varEnum##Symbol == pSubKey->GetNameSymbol() ) \ + { \ + const char *pchParam = pSubKey->GetString(); \ + if ( pchParam && StringHasPrefix( pchParam, "convar " ) ) \ + { \ + ConVarRef tempCVar( pchParam + Q_strlen( "convar " ) ); \ + if ( tempCVar.IsValid() ) \ + { \ + _varName = tempCVar.GetString(); \ + } \ + else \ + { \ + _varName = ""; \ + } \ + } \ + else \ + { \ + _varName = pSubKey->GetString(); \ + } \ + } + +// Gets a scripted variable by offset and casts it to the proper type +#define LESSON_VARIABLE_GET_FROM_OFFSET( _type, _offset ) *static_cast<_type*>( static_cast( static_cast( static_cast( this ) ) + _offset ) ) + + +// Enum of scripted variables +enum LessonVariable +{ + // Run enum macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_ENUM +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_ENUM + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + LESSON_VARIABLE_TOTAL +}; + +// Declare the keyvalues symbols for the keynames +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_SYMBOL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_SYMBOL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + +// String lookup prototypes +LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames = true ); +_fieldtypes LessonParamTypeFromString( const char *pchName ); +int LessonActionFromString( const char *pchName ); + + +// This is used to get type info an variable offsets from the variable enumerated value +class LessonVariableInfo +{ +public: + + LessonVariableInfo() + : iOffset( 0 ), varType( FIELD_VOID ) + { + } + + // Run init info macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_BOOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_STRING + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + +public: + + int iOffset; + _fieldtypes varType; +}; + +LessonVariableInfo g_pLessonVariableInfo[ LESSON_VARIABLE_TOTAL ]; + + +const LessonVariableInfo *GetLessonVariableInfo( int iLessonVariable ) +{ + Assert( iLessonVariable >= 0 && iLessonVariable < LESSON_VARIABLE_TOTAL ); + + if ( g_pLessonVariableInfo[ 0 ].varType == FIELD_VOID ) + { + // Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_INFO_CALL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_INFO_CALL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + return &(g_pLessonVariableInfo[ iLessonVariable ]); +} + +static CUtlDict< LessonVariable, int > g_NameToTypeMap; +static CUtlDict< fieldtype_t, int > g_TypeToParamTypeMap; +CUtlDict< int, int > CScriptedIconLesson::LessonActionMap; + +CScriptedIconLesson::~CScriptedIconLesson() +{ + if ( m_pDefaultHolder ) + { + delete m_pDefaultHolder; + m_pDefaultHolder = NULL; + } +} + + +void CScriptedIconLesson::Init() +{ + m_hLocalPlayer.Set( NULL ); + m_fOutput = 0.0f; + m_hEntity1.Set( NULL ); + m_hEntity2.Set( NULL ); + m_szString1 = ""; + m_szString2 = ""; + m_iInteger1 = 0; + m_iInteger2 = 0; + m_fFloat1 = 0.0f; + m_fFloat2 = 0.0f; + + m_fUpdateEventTime = 0.0f; + m_pDefaultHolder = NULL; + m_iScopeDepth = 0; + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing scripted lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + if ( !IsDefaultHolder() ) + { + if ( !IsOpenOpportunity() ) + { + // Initialize from the key value file + InitFromKeys( GetGameInstructor().GetScriptKeys() ); + + if ( m_iPriority >= LESSON_PRIORITY_MAX ) + { + DevWarning( "Priority level not set for lesson: %s\n", GetName() ); + } + + // We use this to remember variable defaults to be reset before each open attempt + m_pDefaultHolder = new CScriptedIconLesson( GetName(), true, false ); + CScriptedIconLesson *pOpenLesson = m_pDefaultHolder; + + // Run copy macros on all default scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // Listen for open events + for ( int iLessonEvent = 0; iLessonEvent < m_OpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_OpenEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + + // Listen for close events + for ( int iLessonEvent = 0; iLessonEvent < m_CloseEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_CloseEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + + // Listen for success events + for ( int iLessonEvent = 0; iLessonEvent < m_SuccessEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(m_SuccessEvents[ iLessonEvent ]); + ListenForGameEvent( pLessonEvent->szEventName.String()); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tListen for success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ".\n" ); + } + } + } + else + { + // This is an open lesson! Get the root for reference + const CScriptedIconLesson *pLesson = static_cast( GetGameInstructor().GetLesson( GetName() ) ); + SetRoot( const_cast( pLesson ) ); + } + } +} + +void CScriptedIconLesson::InitPrerequisites() +{ + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Initializing prereqs for scripted lesson " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + for ( int iPrerequisit = 0; iPrerequisit < m_PrerequisiteNames.Count(); ++iPrerequisit ) + { + const char *pPrerequisiteName = m_PrerequisiteNames[ iPrerequisit ].String(); + AddPrerequisite( pPrerequisiteName ); + } +} + +void CScriptedIconLesson::OnOpen() +{ + VPROF_BUDGET( "CScriptedIconLesson::OnOpen", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + const CScriptedIconLesson *pLesson = static_cast( GetRoot() ); + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_OnOpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->m_OnOpenEvents[ iLessonEvent ]); + + if ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() ) + { + ConColorMsg( Color( 255, 128, 64, 255 ), "GAME INSTRUCTOR: " ); + ConColorMsg( Color( 64, 128, 255, 255 ), "OnOpen event " ); + ConColorMsg( Color( 0, 255, 0, 255 ), "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( Color( 64, 128, 255, 255 ), "received for lesson \"%s\"...\n", GetName() ); + } + + ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + BaseClass::OnOpen(); +} + +void CScriptedIconLesson::Update() +{ + VPROF_BUDGET( "CScriptedIconLesson::Update", "GameInstructor" ); + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + const CScriptedIconLesson *pLesson = static_cast( GetRoot() ); + + if ( gpGlobals->curtime >= m_fUpdateEventTime ) + { + bool bShowSpew = ( gameinstructor_verbose.GetInt() > 1 && ShouldShowSpew() ); + + int iVerbose = gameinstructor_verbose.GetInt(); + if ( gameinstructor_verbose.GetInt() == 1 ) + { + // Force the verbose level from 1 to 0 for update events + gameinstructor_verbose.SetValue( 0 ); + } + + // Process all update events + for ( int iLessonEvent = 0; iLessonEvent < pLesson->m_UpdateEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pLesson->m_UpdateEvents[ iLessonEvent ]); + + if ( bShowSpew ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Update event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + ProcessElements( NULL, &(pLessonEvent->elements) ); + } + + gameinstructor_verbose.SetValue( iVerbose ); + + // Wait before doing update events again + m_fUpdateEventTime = gpGlobals->curtime + m_fUpdateInterval; + } + + BaseClass::Update(); +} + +void CScriptedIconLesson::SwapOutPlayers( int iOldUserID, int iNewUserID ) +{ + BaseClass::SwapOutPlayers( iOldUserID, iNewUserID ); + + // Get the player pointers from the user IDs + C_BasePlayer *pOldPlayer = UTIL_PlayerByUserId( iOldUserID ); + C_BasePlayer *pNewPlayer = UTIL_PlayerByUserId( iNewUserID ); + + if ( pOldPlayer == m_hEntity1.Get() ) + { + if ( pNewPlayer ) + { + m_hEntity1 = pNewPlayer; + } + else + { + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity1; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } + + if ( pOldPlayer == m_hEntity2.Get() ) + { + if ( pNewPlayer ) + { + m_hEntity2 = pNewPlayer; + } + else + { + + if ( m_iNumDelayedPlayerSwaps < MAX_DELAYED_PLAYER_SWAPS ) + { + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].phHandleToChange = &m_hEntity2; + m_pDelayedPlayerSwap[ m_iNumDelayedPlayerSwaps ].iNewUserID = iNewUserID; + ++m_iNumDelayedPlayerSwaps; + } + } + } +} + +void CScriptedIconLesson::FireGameEvent( IGameEvent *event ) +{ + VPROF_BUDGET( "CScriptedIconLesson::FireGameEvent", "GameInstructor" ); + + if ( m_bDisabled ) + return; + + if ( !DoDelayedPlayerSwaps() ) + { + return; + } + + if ( !C_BasePlayer::GetLocalPlayer() ) + return; + + // Check that this lesson is allowed for the current input device + if( m_bOnlyKeyboard /*&& input->ControllerModeActive()*/ ) + return; + + if( m_bOnlyGamepad /*&& !input->ControllerModeActive()*/ ) + return; + + // Check that this lesson is for the proper team + CBasePlayer *pLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + if ( m_iTeam != TEAM_ANY && pLocalPlayer && pLocalPlayer->GetTeamNumber() != m_iTeam ) + { + // This lesson is intended for a different team + return; + } + + const char *name = event->GetName(); + + // Open events run on the root + ProcessOpenGameEvents( this, name, event ); + + // Close and success events run on the children + const CUtlVector < CBaseLesson * > *pChildren = GetChildren(); + for ( int iChild = 0; iChild < pChildren->Count(); ++iChild ) + { + CScriptedIconLesson *pScriptedChild = dynamic_cast( (*pChildren)[ iChild ] ); + + pScriptedChild->ProcessCloseGameEvents( this, name, event ); + pScriptedChild->ProcessSuccessGameEvents( this, name, event ); + } +} + +void CScriptedIconLesson::ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN && GetGameInstructor().IsLessonOfSameTypeOpen( this ) ) + { + // We don't want more than one of this type, and there is already one open + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Opportunity " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pRootLesson->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "open events NOT processed (there is already an open lesson of this type).\n" ); + } + + return; + } + + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_OpenEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_OpenEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( m_pDefaultHolder ) + { + // Run copy from default macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_DEFAULT +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_DEFAULT + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\tAll elements returned true. Opening!\n" ); + } + + MEM_ALLOC_CREDIT(); + CScriptedIconLesson *pOpenLesson = new CScriptedIconLesson( GetName(), false, true ); + + // Run copy macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_COPY +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_COPY + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + if ( GetGameInstructor().OpenOpportunity( pOpenLesson ) ) + { + pOpenLesson->OnOpen(); + + if ( pRootLesson->InstanceType() == LESSON_INSTANCE_SINGLE_OPEN ) + { + // This one is open and we only want one! So, we're done. + // Other open events may be listening for the same events... skip them! + return; + } + } + } + } + } +} + +void CScriptedIconLesson::ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_CloseEvents.Count() && IsOpenOpportunity(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_CloseEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tAll elements returned true. Closing!\n" ); + } + + CloseOpportunity( "Close event elements completed." ); + } + } + } +} + +void CScriptedIconLesson::ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ) +{ + for ( int iLessonEvent = 0; iLessonEvent < pRootLesson->m_SuccessEvents.Count(); ++iLessonEvent ) + { + const LessonEvent_t *pLessonEvent = &(pRootLesson->m_SuccessEvents[ iLessonEvent ]); + + if ( Q_strcmp( name, pLessonEvent->szEventName.String()) == 0 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseHeader, "GAME INSTRUCTOR: " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "Success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\"", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "received for lesson \"%s\"...\n", GetName() ); + } + + if ( ProcessElements( event, &(pLessonEvent->elements) ) ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\tAll elements returned true. Succeeding!\n" ); + } + + MarkSucceeded(); + } + } + } +} + +LessonVariable LessonVariableFromString( const char *pchName, bool bWarnOnInvalidNames ) +{ + int slot = g_NameToTypeMap.Find( pchName ); + if ( slot != g_NameToTypeMap.InvalidIndex() ) + return g_NameToTypeMap[ slot ]; + + if ( bWarnOnInvalidNames ) + { + AssertMsg( 0, "Invalid scripted lesson variable!" ); + DevWarning( "Invalid scripted lesson variable: %s\n", pchName ); + } + + return LESSON_VARIABLE_TOTAL; +} + +_fieldtypes LessonParamTypeFromString( const char *pchName ) +{ + int slot = g_TypeToParamTypeMap.Find( pchName ); + if ( slot != g_TypeToParamTypeMap.InvalidIndex() ) + return g_TypeToParamTypeMap[ slot ]; + + DevWarning( "Invalid scripted lesson variable/param type: %s\n", pchName ); + return FIELD_VOID; +} + +int LessonActionFromString( const char *pchName ) +{ + int slot = CScriptedIconLesson::LessonActionMap.Find( pchName ); + if ( slot != CScriptedIconLesson::LessonActionMap.InvalidIndex() ) + return CScriptedIconLesson::LessonActionMap[ slot ]; + + DevWarning( "Invalid scripted lesson action: %s\n", pchName ); + return LESSON_ACTION_NONE; +} + +void CScriptedIconLesson::InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ) +{ + KeyValues *pSubKey = NULL; + for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + char szSubKeyName[ 256 ]; + Q_strcpy( szSubKeyName, pSubKey->GetName() ); + + char *pchToken = strtok( szSubKeyName, " " ); + LessonVariable iVariable = LessonVariableFromString( pchToken ); + + pchToken = strtok ( NULL, "" ); + int iAction = LESSON_ACTION_NONE; + bool bNot = false; + bool bOptionalParam = false; + + if ( !pchToken || pchToken[ 0 ] == '\0' ) + { + DevWarning( "No action specified for variable: \"%s\"\n", pSubKey->GetName() ); + } + else + { + if ( pchToken[ 0 ] == '?' ) + { + pchToken++; + bOptionalParam = true; + } + + if ( pchToken[ 0 ] == '!' ) + { + pchToken++; + bNot = true; + } + + iAction = LessonActionFromString( pchToken ); + } + + Q_strcpy( szSubKeyName, pSubKey->GetString() ); + + pchToken = strtok( szSubKeyName, " " ); + _fieldtypes paramType = LessonParamTypeFromString( pchToken ); + + char *pchParam = ""; + + if ( paramType != FIELD_VOID ) + { + pchToken = strtok ( NULL, "" ); + pchParam = pchToken; + } + + if ( !pchParam ) + { + DevWarning( "No parameter specified for action: \"%s\"\n", pSubKey->GetName() ); + } + else + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t\tElement \"%s %s\" added.\n", pSubKey->GetName(), pSubKey->GetString() ); + } + + // See if our param is a scripted var + LessonVariable iParamVarIndex = LessonVariableFromString( pchParam, false ); + + pLessonElements->AddToTail( LessonElement_t( iVariable, iAction, bNot, bOptionalParam, pchParam, iParamVarIndex, paramType ) ); + } + } +} + +void CScriptedIconLesson::InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ) +{ + for ( int i = 0; i < pLessonElements2->Count(); ++i ) + { + pLessonElements->AddToTail( LessonElement_t( (*pLessonElements2)[ i ] ) ); + } +} + +void CScriptedIconLesson::InitFromKeys( KeyValues *pKey ) +{ + if ( !pKey ) + return; + + static int s_nInstanceTypeSymbol = KeyValuesSystem()->GetSymbolForString( "instance_type" ); + static int s_nReplaceKeySymbol = KeyValuesSystem()->GetSymbolForString( "replace_key" ); + static int s_nFixedInstancesMaxSymbol = KeyValuesSystem()->GetSymbolForString( "fixed_instances_max" ); + static int s_nReplaceOnlyWhenStopped = KeyValuesSystem()->GetSymbolForString( "replace_only_when_stopped" ); + static int s_nTeamSymbol = KeyValuesSystem()->GetSymbolForString( "team" ); + static int s_nOnlyKeyboardSymbol = KeyValuesSystem()->GetSymbolForString( "only_keyboard" ); + static int s_nOnlyGamepadSymbol = KeyValuesSystem()->GetSymbolForString( "only_gamepad" ); + static int s_nDisplayLimitSymbol = KeyValuesSystem()->GetSymbolForString( "display_limit" ); + static int s_nSuccessLimitSymbol = KeyValuesSystem()->GetSymbolForString( "success_limit" ); + static int s_nPreReqSymbol = KeyValuesSystem()->GetSymbolForString( "prereq" ); + static int s_nOpenSymbol = KeyValuesSystem()->GetSymbolForString( "open" ); + static int s_nCloseSymbol = KeyValuesSystem()->GetSymbolForString( "close" ); + static int s_nSuccessSymbol = KeyValuesSystem()->GetSymbolForString( "success" ); + static int s_nOnOpenSymbol = KeyValuesSystem()->GetSymbolForString( "onopen" ); + static int s_nUpdateSymbol = KeyValuesSystem()->GetSymbolForString( "update" ); + + KeyValues *pSubKey = NULL; + for ( pSubKey = pKey->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) + { + if ( pSubKey->GetNameSymbol() == s_nInstanceTypeSymbol ) + { + m_iInstanceType = LessonInstanceType( pSubKey->GetInt() ); + } + else if ( pSubKey->GetNameSymbol() == s_nReplaceKeySymbol ) + { + m_stringReplaceKey = pSubKey->GetString(); + } + else if ( pSubKey->GetNameSymbol() == s_nFixedInstancesMaxSymbol ) + { + m_iFixedInstancesMax = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nReplaceOnlyWhenStopped ) + { + m_bReplaceOnlyWhenStopped = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nTeamSymbol ) + { + m_iTeam = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nOnlyKeyboardSymbol ) + { + m_bOnlyKeyboard = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nOnlyGamepadSymbol ) + { + m_bOnlyGamepad = pSubKey->GetBool(); + } + else if ( pSubKey->GetNameSymbol() == s_nDisplayLimitSymbol ) + { + m_iDisplayLimit = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nSuccessLimitSymbol ) + { + m_iSuccessLimit = pSubKey->GetInt(); + } + else if ( pSubKey->GetNameSymbol() == s_nPreReqSymbol ) + { + CGameInstructorSymbol pName; + pName = pSubKey->GetString(); + m_PrerequisiteNames.AddToTail( pName ); + } + else if ( pSubKey->GetNameSymbol() == s_nOpenSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddOpenEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding open event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nCloseSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddCloseEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding close event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nSuccessSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddSuccessEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding success event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nOnOpenSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddOnOpenEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding onopen event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseOpen, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + else if ( pSubKey->GetNameSymbol() == s_nUpdateSymbol ) + { + KeyValues *pEventKey = NULL; + for ( pEventKey = pSubKey->GetFirstTrueSubKey(); pEventKey; pEventKey = pEventKey->GetNextTrueSubKey() ) + { + LessonEvent_t *pLessonEvent = AddUpdateEvent(); + pLessonEvent->szEventName = pEventKey->GetName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tAdding update event " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseUpdate, "\"%s\" ", pLessonEvent->szEventName.String()); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "...\n" ); + } + + InitElementsFromKeys( &(pLessonEvent->elements), pEventKey ); + } + } + + // Run intialize from key macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_BOOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_STRING + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } +} + +bool CScriptedIconLesson::ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ) +{ + VPROF_BUDGET( "CScriptedIconLesson::ProcessElements", "GameInstructor" ); + + m_hLocalPlayer = GetGameInstructor().GetLocalPlayer(); + + bool bSuccess = true; + int nContinueScope = -1; + m_iScopeDepth = 0; + + if ( gameinstructor_find_errors.GetBool() ) + { + // Just run them all to check for errors! + for ( int iElement = 0; iElement < pElements->Count(); ++iElement ) + { + ProcessElement( event, &((*pElements)[ iElement ] ), false ); + } + + return false; + } + + // Process each element until a step fails + for ( int iElement = 0; iElement < pElements->Count(); ++iElement ) + { + if ( nContinueScope == m_iScopeDepth ) + { + nContinueScope = -1; + } + + if ( !ProcessElement( event, &((*pElements)[ iElement ]), nContinueScope != -1 ) ) + { + // This element failed + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tPrevious element returned false.\n" ); + } + + nContinueScope = m_iScopeDepth - 1; + + if ( nContinueScope < 0 ) + { + // No outer scope to worry about, we're done + bSuccess = false; + break; + } + } + } + + return bSuccess; +} + +bool CScriptedIconLesson::ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope ) +{ + VPROF_BUDGET( "CScriptedIconLesson::ProcessElement", "GameInstructor" ); + + if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_IN ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeIn()\n" ); + } + + m_iScopeDepth++; + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_SCOPE_OUT ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tScopeOut()\n" ); + } + + m_iScopeDepth--; + return true; + } + + if ( bInFailedScope ) + { + // Only scope upkeep is done when we're in a failing scope... bail! + return true; + } + + if ( pLessonElement->iAction == LESSON_ACTION_CLOSE ) + { + // Special case for closing (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tCloseOpportunity()\n" ); + } + + CloseOpportunity( "Close action." ); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_SUCCESS ) + { + // Special case for succeeding (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tMarkSucceeded()\n" ); + } + + MarkSucceeded(); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_LOCK ) + { + // Special case for setting the starting point for the lesson to stay locked from (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tm_fLockTime = gpGlobals->curtime\n" ); + } + + m_fLockTime = gpGlobals->curtime; + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_COMPLETE ) + { + // Special case for checking presentation status (we don't need variables for this) + bool bPresentComplete = IsPresentComplete(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tIsPresentComplete() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s ", ( bPresentComplete ) ? ( "true" ) : ( "false" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( pLessonElement->bNot ) ? ( "!= true\n" ) : ( "== true\n" ) ); + } + + return ( pLessonElement->bNot ) ? ( !bPresentComplete ) : ( bPresentComplete ); + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_START ) + { + // Special case for setting presentation status (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentStart()\n" ); + } + + PresentStart(); + return true; + } + else if ( pLessonElement->iAction == LESSON_ACTION_PRESENT_END ) + { + // Special case for setting presentation status (we don't need variables for this) + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPresentEnd()\n" ); + } + + PresentEnd(); + return true; + } + + // These values temporarily hold the parameter's value + const char *pParamName = pLessonElement->szParam.String(); + float eventParam_float = 0.0f; + char eventParam_string[ 256 ]; + eventParam_string[ 0 ] = '\0'; + C_BaseEntity *eventParam_BaseEntity = NULL; + + // Get the value from the event parameter based on its type + switch ( pLessonElement->paramType ) + { + case FIELD_FLOAT: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ); + break; + case FIELD_INTEGER: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ); + break; + case FIELD_BOOLEAN: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ); + break; + case FIELD_STRING: + eventParam_float = static_cast( atoi( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ) ); + break; + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = event->GetFloat( pParamName ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' || pParamName[ 0 ] == '.' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = Q_atof( pParamName ); + } + else + { + DevWarning( "Invalid event field name and not a float \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_INTEGER: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ); + break; + case FIELD_INTEGER: + eventParam_float = LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ); + break; + case FIELD_BOOLEAN: + eventParam_float = static_cast( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ); + break; + case FIELD_STRING: + eventParam_float = atof( &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String() ); + break; + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = static_cast( event->GetInt( pParamName ) ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( ( pParamName[ 0 ] >= '0' && pParamName[ 0 ] <= '9' ) || pParamName[ 0 ] == '-' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = static_cast( Q_atoi( pParamName ) ); + } + else + { + DevWarning( "Invalid event field name and not an integer \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_STRING: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_STRING: + Q_strncpy( eventParam_string, &LESSON_VARIABLE_GET_FROM_OFFSET( CGameInstructorSymbol, pInfo->iOffset )->String(), sizeof( eventParam_string ) ); + break; + case FIELD_FLOAT: + Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%f", LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ); + break; + case FIELD_INTEGER: + Q_snprintf( eventParam_string, sizeof( eventParam_string ), "%i", LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ); + break; + case FIELD_BOOLEAN: + case FIELD_EHANDLE: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else + { + const char *pchEventString = NULL; + + if ( event && !(event->IsEmpty( pParamName )) ) + { + pchEventString = event->GetString( pParamName ); + } + + if ( pchEventString && pchEventString[0] ) + { + Q_strcpy( eventParam_string, pchEventString ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else + { + // This param doesn't exist, try parsing the string + Q_strncpy( eventParam_string, pParamName, sizeof( eventParam_string ) ); + } + } + break; + + case FIELD_BOOLEAN: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_FLOAT: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( float, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_INTEGER: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( int, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_BOOLEAN: + eventParam_float = ( ( LESSON_VARIABLE_GET_FROM_OFFSET( bool, pInfo->iOffset ) ) ? ( 1.0f ) : ( 0.0f ) ); + break; + case FIELD_EHANDLE: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_float = ( event->GetBool( pParamName ) ) ? ( 1.0f ) : ( 0.0f ); + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( pParamName[ 0 ] == '0' || pParamName[ 0 ] == '1' ) + { + // This param doesn't exist, try parsing the string + eventParam_float = Q_atof( pParamName ) != 0.0f; + } + else + { + DevWarning( "Invalid event field name and not an boolean \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_CUSTOM: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_EHANDLE: + eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get(); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + break; + case FIELD_FLOAT: + case FIELD_INTEGER: + case FIELD_BOOLEAN: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + eventParam_BaseEntity = UTIL_PlayerByUserId( event->GetInt( pParamName ) ); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tPlayer param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( Q_stricmp( pParamName, "null" ) == 0 ) + { + // They explicitly want a null pointer + eventParam_BaseEntity = NULL; + } + else + { + DevWarning( "Invalid event field name \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_EHANDLE: + if ( pLessonElement->iParamVarIndex < LESSON_VARIABLE_TOTAL ) + { + // The parameter is a scripted var + const LessonVariableInfo *pInfo = GetLessonVariableInfo( pLessonElement->iParamVarIndex ); + + switch ( pInfo->varType ) + { + case FIELD_EHANDLE: + eventParam_BaseEntity = ( LESSON_VARIABLE_GET_FROM_OFFSET( EHANDLE, pInfo->iOffset ) ).Get(); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + break; + case FIELD_FLOAT: + case FIELD_INTEGER: + case FIELD_BOOLEAN: + case FIELD_STRING: + case FIELD_FUNCTION: + DevWarning( "Can't use this variable type with this parameter type in lesson script.\n" ); + break; + } + } + else if ( event && !(event->IsEmpty( pParamName )) ) + { + int iEntID = event->GetInt( pParamName ); + if ( iEntID >= NUM_ENT_ENTRIES ) + { + AssertMsg( 0, "Invalid entity ID used in game event field!" ); + DevWarning( "Invalid entity ID used in game event (%s) for param (%s).", event->GetName(), pParamName ); + return false; + } + + eventParam_BaseEntity = C_BaseEntity::Instance( iEntID ); + if ( !eventParam_BaseEntity ) + { + if ( pLessonElement->bOptionalParam ) + { + // Not having an entity is fine + return true; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tEntity param \"%s\" returned NULL.\n", pParamName ); + } + return false; + } + } + else if ( pLessonElement->bOptionalParam ) + { + // We don't want to interpret this and not finding the param is still ok + return true; + } + else if ( Q_stricmp( pParamName, "null" ) == 0 ) + { + // They explicitly want a null pointer + eventParam_BaseEntity = NULL; + } + else if ( Q_stricmp( pParamName, "world" ) == 0 ) + { + // They explicitly want the world + eventParam_BaseEntity = GetClientWorldEntity(); + } + else + { + DevWarning( "Invalid event field name \"%s\".\n", pParamName ); + return false; + } + break; + + case FIELD_EMBEDDED: + { + // The parameter is a convar + ConVarRef tempCVar( pParamName ); + if ( tempCVar.IsValid() ) + { + eventParam_float = tempCVar.GetFloat(); + Q_strncpy( eventParam_string, tempCVar.GetString(), sizeof( eventParam_string ) ); + } + else + { + DevWarning( "Invalid convar name \"%s\".\n", pParamName ); + return false; + } + } + break; + } + + // Do the action to the specified variable + switch ( pLessonElement->iVariable ) + { + // Run process action macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO PROCESS_LESSON_ACTION +#define LESSON_VARIABLE_MACRO_BOOL PROCESS_LESSON_ACTION +#define LESSON_VARIABLE_MACRO_EHANDLE PROCESS_LESSON_ACTION_EHANDLE +#define LESSON_VARIABLE_MACRO_STRING PROCESS_LESSON_ACTION_STRING + LESSON_VARIABLE_FACTORY; +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + } + + return true; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &fVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + switch ( iAction ) + { + case LESSON_ACTION_SET: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar = fParam; + return true; + + case LESSON_ACTION_ADD: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] += [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar += fParam; + return true; + + case LESSON_ACTION_SUBTRACT: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] -= [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar -= fParam; + return true; + + case LESSON_ACTION_MULTIPLY: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] *= [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + fVar *= fParam; + return true; + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + return ( bNot ) ? ( fVar != fParam ) : ( fVar == fParam ); + + case LESSON_ACTION_LESS_THAN: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", fVar ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + return ( bNot ) ? ( fVar >= fParam ) : ( fVar < fParam ); + + case LESSON_ACTION_HAS_BIT: + { + int iTemp1 = static_cast( fVar ); + int iTemp2 = ( 1 << static_cast( fParam ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "& [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "0x%X", iTemp2 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == 0\n" ) : ( ") != 0\n" ) ); + } + + return ( bNot ) ? ( ( iTemp1 & iTemp2 ) == 0 ) : ( ( iTemp1 & iTemp2 ) != 0 ); + } + + case LESSON_ACTION_BIT_COUNT_IS: + { + int iTemp1 = UTIL_CountNumBitsSet( static_cast( fVar ) ); + int iTemp2 = static_cast( fParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " != [%s] " ) : ( " == [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 ); + } + + return ( bNot ) ? ( iTemp1 != iTemp2 ) : ( iTemp1 == iTemp2 ); + } + + case LESSON_ACTION_BIT_COUNT_LESS_THAN: + { + int iTemp1 = UTIL_CountNumBitsSet( static_cast( fVar ) ); + int iTemp2 = static_cast( fParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tUTIL_CountNumBitsSet([%s]) ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp1 ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( " >= [%s] " ) : ( " < [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp2 ); + } + + return ( bNot ) ? ( iTemp1 >= iTemp2 ) : ( iTemp1 < iTemp2 ); + } + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &iVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + float fTemp = static_cast( iVar ); + bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam ); + + iVar = static_cast( fTemp ); + return bRetVal; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam ) +{ + float fTemp = ( bVar ) ? ( 1.0f ) : ( 0.0f ); + bool bRetVal = ProcessElementAction( iAction, bNot, pchVarName, fTemp, pchParamName, fParam ); + + bVar = ( fTemp != 0.0f ); + return bRetVal; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam ) +{ + // First try to let the mod act on the action + /*bool bModHandled = false; + bool bModReturn = Mod_ProcessElementAction( iAction, bNot, pchVarName, hVar, pchParamName, fParam, pParam, pchParam, bModHandled ); + + if ( bModHandled ) + { + return bModReturn; + }*/ + + C_BaseEntity *pVar = hVar.Get(); + + switch ( iAction ) + { + case LESSON_ACTION_SET: + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]\n", pchVarName, pchParamName->String() ); + } + + hVar = pParam; + return true; + } + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t[%s] != [%s]\n" ) : ( "\t[%s] == [%s]\n" ), pchVarName, pchParamName->String() ); + } + + return ( bNot ) ? ( pVar != pParam ) : ( pVar == pParam ); + + case LESSON_ACTION_GET_DISTANCE: + { + if ( !pVar || !pParam ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] )", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" ); + } + + return false; + } + + C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL ); + C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL ); + + Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() ); + Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() ); + + m_fOutput = vVarPos.DistTo( vParamPos ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->DistTo( [%s] ) ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + } + + return true; + } + + case LESSON_ACTION_GET_ANGULAR_DISTANCE: + { + if ( !pVar || !pParam ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] )", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle or Param handle returned NULL!\n" ); + } + + return false; + } + + C_BasePlayer *pVarPlayer = ( pVar->IsPlayer() ? static_cast< C_BasePlayer* >( pVar ) : NULL ); + C_BasePlayer *pParamPlayer = ( pParam->IsPlayer() ? static_cast< C_BasePlayer* >( pParam ) : NULL ); + + Vector vVarPos = ( pVarPlayer ? pVarPlayer->EyePosition() : pVar->WorldSpaceCenter() ); + Vector vParamPos = ( pParamPlayer ? pParamPlayer->EyePosition() : pParam->WorldSpaceCenter() ); + + Vector vVarToParam = vParamPos - vVarPos; + VectorNormalize( vVarToParam ); + + Vector vVarForward; + + if ( pVar->IsPlayer() ) + { + AngleVectors( static_cast< C_BasePlayer* >( pVar )->EyeAngles(), &vVarForward, NULL, NULL ); + } + else + { + pVar->GetVectors( &vVarForward, NULL, NULL ); + } + + // Set the distance in degrees + m_fOutput = ( vVarToParam.Dot( vVarForward ) - 1.0f ) * -90.0f; + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->AngularDistTo( [%s] ) ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + } + + return true; + } + + case LESSON_ACTION_GET_PLAYER_DISPLAY_NAME: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [stringINVALID], [%s]->GetPlayerName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use string2 if it was specified, otherwise, use string1 + CGameInstructorSymbol *pString; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pString = &m_szString2; + pchParamNameTemp = "string2"; + } + else + { + pString = &m_szString1; + pchParamNameTemp = "string1"; + } + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + *pString = pVar->GetPlayerName(); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy( [%s], [%s]->GetPlayerName() ", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pString->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return true; + } + + case LESSON_ACTION_CLASSNAME_IS: + { + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t!FClassnameIs( [%s] " ) : ( "\tFClassnameIs( [%s] " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pVar->GetClassname() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return ( bNot ) ? ( !FClassnameIs( pVar, pchParam ) ) : ( FClassnameIs( pVar, pchParam ) ); + } + + case LESSON_ACTION_TEAM_IS: + { + int iTemp = static_cast( fParam ); + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetTeamNumber() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetTeamNumber() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + } + + return ( bNot ) ? ( pVar->GetTeamNumber() != iTemp ) : ( pVar->GetTeamNumber() == iTemp ); + } + + case LESSON_ACTION_MODELNAME_IS: + { + C_BaseAnimating *pBaseAnimating = dynamic_cast( pVar ); + + if ( !pBaseAnimating ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "..." ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseAnimating returned NULL!\n" ); + } + + return false; + } + + const char *pchModelName = "-no model-"; + CStudioHdr *pModel = pBaseAnimating->GetModelPtr(); + if ( pModel ) + { + const studiohdr_t *pRenderHDR = pModel->GetRenderHdr(); + if ( pRenderHDR ) + { + pchModelName = pRenderHDR->name; + } + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_stricmp( [%s]->ModelName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%s", pchModelName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + } + + return ( bNot ) ? ( Q_stricmp( pchModelName, pchParam ) != 0 ) : ( Q_stricmp( pchModelName, pchParam ) == 0 ); + } + + case LESSON_ACTION_HEALTH_LESS_THAN: + { + int iTemp = static_cast( fParam ); + + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetHealth() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", pVar->GetHealth() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i\n", iTemp ); + } + + return ( bNot ) ? ( pVar->GetHealth() >= iTemp ) : ( pVar->GetHealth() < iTemp ); + } + + case LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN: + { + if ( !pVar ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", pVar->HealthFraction() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam ); + } + + float fHealthPercentage = 1.0f; + + if ( pVar->GetMaxHealth() != 0.0f ) + { + fHealthPercentage = pVar->HealthFraction(); + } + + return ( bNot ) ? ( fHealthPercentage >= fParam ) : ( fHealthPercentage < fParam ); + } + + case LESSON_ACTION_GET_ACTIVE_WEAPON: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = [%s]->GetActiveWeapon()\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( pBaseCombatCharacter->GetActiveWeapon() ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = [%s]->GetActiveWeapon()", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + } + + return true; + } + + case LESSON_ACTION_WEAPON_IS: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->GetName() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\" ", pBaseCombatWeapon->GetName() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "!= [%s] " ) : ( "== [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"\n", pchParam ); + } + + return ( bNot ) ? ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) != 0 ) : ( Q_stricmp( pBaseCombatWeapon->GetName(), pchParam ) == 0 ); + } + + case LESSON_ACTION_WEAPON_HAS: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_OwnsThisType([%s] " ) : ( "\t[%s]->Weapon_OwnsThisType([%s] " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return ( bNot ) ? ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) == NULL ) : ( pBaseCombatCharacter->Weapon_OwnsThisType( pchParam ) != NULL ); + } + + case LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + C_BaseCombatWeapon *pWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetActiveSlot() ...\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + // @TODO + /*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pWeapon->GetWpnData().szClassName ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + }*/ + + return true; + } + + case LESSON_ACTION_GET_WEAPON_SLOT: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") ...\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + // @TODO + /*m_fOutput = pBaseCombatCharacter->Weapon_GetSlot( pchParam ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[output] = [%s]->Weapon_GetSlot([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ") " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", m_fOutput ); + }*/ + + return true; + } + + case LESSON_ACTION_GET_WEAPON_IN_SLOT: + { + int nTemp = static_cast( fParam ); + + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + m_hEntity1 = pBaseCombatCharacter->GetWeapon( nTemp ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entity1] = [%s]->GetWeapon([%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%i\"", nTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + return true; + } + + case LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN: + { + C_BaseCombatCharacter *pBaseCombatCharacter = NULL; + + if ( pVar ) + { + pBaseCombatCharacter = pVar->MyCombatCharacterPointer(); + } + + if ( !pBaseCombatCharacter ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BaseCombatCharacter returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = pBaseCombatCharacter->GetActiveWeapon(); + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + float fClip1Percentage = 100.0f; + + if ( pBaseCombatWeapon->UsesClipsForAmmo1() ) + { + fClip1Percentage = 100.0f * ( static_cast( pBaseCombatWeapon->Clip1() ) / static_cast( pBaseCombatWeapon->GetMaxClip1() ) ); + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetActiveWeapon()->Clip1Percentage() ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f ", fClip1Percentage ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%.1f\n", fParam ); + } + + return ( bNot ) ? ( fClip1Percentage >= fParam ) : ( fClip1Percentage < fParam ); + } + + case LESSON_ACTION_WEAPON_AMMO_LOW: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30\n" ) : ( ")->AmmoPercentage() < 30\n" ) ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetActiveWeapon returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is full + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ ); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoLow = ( iPlayerAmmo < ( iMaxAmmo / 3 ) ); + + if ( bNot ) + { + bAmmoLow = !bAmmoLow; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->GetWeaponInSlot( ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ")->AmmoPercentage() >= 30 " ) : ( ")->AmmoPercentage() < 30 " ) ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bAmmoLow ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return bAmmoLow; + } + + case LESSON_ACTION_WEAPON_AMMO_FULL: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is full + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iMaxAmmo = GetAmmoDef()->MaxCarry( iAmmoType/*, pBasePlayer*/ ); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoFull = ( iPlayerAmmo >= iMaxAmmo ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoFull() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoFull ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return ( bNot ) ? ( !bAmmoFull ) : ( bAmmoFull ); + } + + case LESSON_ACTION_WEAPON_AMMO_EMPTY: + { + int iTemp = static_cast( fParam ); + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + CBaseCombatWeapon *pBaseCombatWeapon = NULL; + + // Get the weapon in variable slot + for ( int iWeapon = 0; iWeapon < MAX_WEAPONS; iWeapon++ ) + { + CBaseCombatWeapon *pBaseCombatWeaponTemp = pBasePlayer->GetWeapon( iWeapon ); + if ( pBaseCombatWeaponTemp ) + { + if ( pBaseCombatWeaponTemp->GetSlot() == iTemp ) + { + pBaseCombatWeapon = pBaseCombatWeaponTemp; + break; + } + } + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty()\n" ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar GetWeaponInSlot returned NULL!\n" ); + } + + return false; + } + + // Check if the ammo is empty + int iAmmoType = pBaseCombatWeapon->GetPrimaryAmmoType(); + int iPlayerAmmo = pBasePlayer->GetAmmoCount( iAmmoType ); + + bool bAmmoEmpty = ( iPlayerAmmo <= 0 ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->GetWeaponInSlot( " ) : ( "\t[%s]->GetWeaponInSlot( " ), pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseSuccess, "%i ", iTemp ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")->AmmoEmpty() " ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bAmmoEmpty ) ? ( "true" ) : ( "false" ) ); + ConColorMsg(CBaseLesson::m_rgbaVerbosePlain, " )\n" ); + } + + return ( bNot ) ? ( !bAmmoEmpty ) : ( bAmmoEmpty ); + } + + /*case LESSON_ACTION_WEAPON_CAN_USE: + { + C_BaseCombatWeapon *pBaseCombatWeapon = dynamic_cast( pParam ); + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as BasePlayer returned NULL!\n" ); + } + + return false; + } + + if ( !pBaseCombatWeapon ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s])\n" ) : ( "\t[%s]->Weapon_CanUse([%s])\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam BaseCombatWeapon returned NULL!\n" ); + } + + return false; + } + + bool bCanEquip = pBasePlayer->Weapon_CanUse( pBaseCombatWeapon ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\t![%s]->Weapon_CanUse([%s]) " ) : ( "\t[%s]->Weapon_CanUse([%s]) " ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, ( bCanEquip ) ? ( "true\n" ) : ( "false\n" ) ); + } + + return ( bNot ) ? ( !bCanEquip ) : ( bCanEquip ); + }*/ + + case LESSON_ACTION_USE_TARGET_IS: + { + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) != [%s]\n" ) : ( "\tC_BaseEntity::Instance([%s]->GetUseEntity()) == [%s]\n" ), pchVarName, pchParamName->String() ); + } + + return ( bNot ) ? ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) != pParam ) : ( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) == pParam ); + } + + case LESSON_ACTION_GET_USE_TARGET: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetUseEntity() ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetUseEntity())\n", pchParamNameTemp, pchVarName ); + } + + return true; + } + + /*case LESSON_ACTION_GET_POTENTIAL_USE_TARGET: + { + int iTemp = static_cast( fParam ); + + if ( iTemp <= 0 || iTemp > 2 ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[entityINVALID] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tParam selecting string is out of range!\n" ); + } + + return false; + } + + // Use entity2 if it was specified, otherwise, use entity1 + CHandle *pHandle; + char const *pchParamNameTemp = NULL; + + if ( iTemp == 2 ) + { + pHandle = &m_hEntity2; + pchParamNameTemp = "entity2"; + } + else + { + pHandle = &m_hEntity1; + pchParamNameTemp = "entity1"; + } + + C_BasePlayer *pBasePlayer = ToBasePlayer( pVar ); + + if ( !pBasePlayer ) + { + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseClose, "\tVar handle as Player returned NULL!\n" ); + } + + return false; + } + + pHandle->Set( C_BaseEntity::Instance( pBasePlayer->GetPotentialUseEntity() ) ); + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s] = C_BaseEntity::Instance([%s]->GetPotentialUseEntity())\n", pchParamNameTemp, pchVarName ); + } + + return true; + }*/ + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam ) +{ + switch ( iAction ) + { + case LESSON_ACTION_REFERENCE_OPEN: + { + const CBaseLesson *pLesson = GetGameInstructor().GetLesson( pchParamName->String() ); + if ( !pLesson ) + { + DevWarning( "Invalid lesson specified: \"%s\".", pchParamName->String() ); + return false; + } + + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( Color( 64, 128, 255, 255 ), ( bNot ) ? ( "\t!( [\"%s\"]->IsInstanceActive() " ) : ( "\t( [\"%s\"]->IsInstanceActive() " ), pchParamName->String() ); + ConColorMsg( Color( 255, 255, 255, 255 ), "\"%s\"", (pLesson->IsInstanceActive() ? "true" : "false") ); + ConColorMsg( Color( 64, 128, 255, 255 ), " )\n" ); + } + + return ( bNot ) ? ( !pLesson->IsInstanceActive() ) : ( pLesson->IsInstanceActive() ); + } + + case LESSON_ACTION_SET: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcpy([%s], [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + *pchVar = pchParam; + return true; + + case LESSON_ACTION_ADD: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcat([%s], [%s] ", pchVarName, pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ")\n" ); + } + + char szTemp[ 256 ]; + Q_strncpy( szTemp, pchVar->String(), sizeof( szTemp ) ); + Q_strncat( szTemp, pchParam, sizeof( szTemp ) ); + + *pchVar = szTemp; + return true; + + case LESSON_ACTION_IS: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") != 0\n" ) : ( ") == 0\n" ) ); + } + + return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) != 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) == 0 ); + + case LESSON_ACTION_HAS_PREFIX: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tStringHasPrefix([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerboseName, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") == false\n" ) : ( ") == true\n" ) ); + } + + return ( bNot ) ? ( !StringHasPrefix( pchVar->String(), pchParam ) ) : ( StringHasPrefix( pchVar->String(), pchParam ) ); + + case LESSON_ACTION_LESS_THAN: + if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() ) + { + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\tQ_strcmp([%s] ", pchVarName ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchVar->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ", [%s] ", pchParamName->String() ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\"%s\"", pchParam ); + ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ") >= 0\n" ) : ( ") < 0\n" ) ); + } + + return ( bNot ) ? ( Q_strcmp( pchVar->String(), pchParam ) >= 0 ) : ( Q_strcmp( pchVar->String(), pchParam ) < 0 ); + } + + DevWarning( "Invalid lesson action type used with \"%s\" variable type.\n", pchVarName ); + + return false; +} + +LessonEvent_t * CScriptedIconLesson::AddOpenEvent() +{ + int iNewLessonEvent = m_OpenEvents.AddToTail(); + return &(m_OpenEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddCloseEvent() +{ + int iNewLessonEvent = m_CloseEvents.AddToTail(); + return &(m_CloseEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddSuccessEvent() +{ + int iNewLessonEvent = m_SuccessEvents.AddToTail(); + return &(m_SuccessEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddOnOpenEvent() +{ + int iNewLessonEvent = m_OnOpenEvents.AddToTail(); + return &(m_OnOpenEvents[ iNewLessonEvent ]); +} + +LessonEvent_t * CScriptedIconLesson::AddUpdateEvent() +{ + int iNewLessonEvent = m_UpdateEvents.AddToTail(); + return &(m_UpdateEvents[ iNewLessonEvent ]); +} + +// Static method to init the keyvalues symbols used for comparisons +void CScriptedIconLesson::PreReadLessonsFromFile() +{ + static bool bFirstTime = true; + if ( !bFirstTime ) + return; + bFirstTime = false; + + // Run init info call macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_BOOL LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_VARIABLE_INIT_SYMBOL +#define LESSON_VARIABLE_MACRO_STRING LESSON_VARIABLE_INIT_SYMBOL + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // And build the map of variable name to enum + // Run string to int macros on all scriptable variables (see: LESSON_VARIABLE_FACTORY definition) +#define LESSON_VARIABLE_MACRO LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_BOOL LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_EHANDLE LESSON_SCRIPT_STRING_ADD_TO_MAP +#define LESSON_VARIABLE_MACRO_STRING LESSON_SCRIPT_STRING_ADD_TO_MAP + LESSON_VARIABLE_FACTORY +#undef LESSON_VARIABLE_MACRO +#undef LESSON_VARIABLE_MACRO_BOOL +#undef LESSON_VARIABLE_MACRO_EHANDLE +#undef LESSON_VARIABLE_MACRO_STRING + + // Set up mapping of field types + g_TypeToParamTypeMap.Insert( "float", FIELD_FLOAT ); + g_TypeToParamTypeMap.Insert( "string", FIELD_STRING ); + g_TypeToParamTypeMap.Insert( "int", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "integer", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "short", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "long", FIELD_INTEGER ); + g_TypeToParamTypeMap.Insert( "bool", FIELD_BOOLEAN ); + g_TypeToParamTypeMap.Insert( "player", FIELD_CUSTOM ); + g_TypeToParamTypeMap.Insert( "entity", FIELD_EHANDLE ); + g_TypeToParamTypeMap.Insert( "convar", FIELD_EMBEDDED ); + g_TypeToParamTypeMap.Insert( "void", FIELD_VOID ); + + // Set up the lesson action map + + CScriptedIconLesson::LessonActionMap.Insert( "scope in", LESSON_ACTION_SCOPE_IN ); + CScriptedIconLesson::LessonActionMap.Insert( "scope out", LESSON_ACTION_SCOPE_OUT ); + CScriptedIconLesson::LessonActionMap.Insert( "close", LESSON_ACTION_CLOSE ); + CScriptedIconLesson::LessonActionMap.Insert( "success", LESSON_ACTION_SUCCESS ); + CScriptedIconLesson::LessonActionMap.Insert( "lock", LESSON_ACTION_LOCK ); + CScriptedIconLesson::LessonActionMap.Insert( "present complete", LESSON_ACTION_PRESENT_COMPLETE ); + CScriptedIconLesson::LessonActionMap.Insert( "present start", LESSON_ACTION_PRESENT_START ); + CScriptedIconLesson::LessonActionMap.Insert( "present end", LESSON_ACTION_PRESENT_END ); + + CScriptedIconLesson::LessonActionMap.Insert( "reference open", LESSON_ACTION_REFERENCE_OPEN ); + + CScriptedIconLesson::LessonActionMap.Insert( "set", LESSON_ACTION_SET ); + CScriptedIconLesson::LessonActionMap.Insert( "add", LESSON_ACTION_ADD ); + CScriptedIconLesson::LessonActionMap.Insert( "subtract", LESSON_ACTION_SUBTRACT ); + CScriptedIconLesson::LessonActionMap.Insert( "multiply", LESSON_ACTION_MULTIPLY ); + CScriptedIconLesson::LessonActionMap.Insert( "is", LESSON_ACTION_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "less than", LESSON_ACTION_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "has prefix", LESSON_ACTION_HAS_PREFIX ); + CScriptedIconLesson::LessonActionMap.Insert( "has bit", LESSON_ACTION_HAS_BIT ); + CScriptedIconLesson::LessonActionMap.Insert( "bit count is", LESSON_ACTION_BIT_COUNT_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "bit count less than", LESSON_ACTION_BIT_COUNT_LESS_THAN ); + + CScriptedIconLesson::LessonActionMap.Insert( "get distance", LESSON_ACTION_GET_DISTANCE ); + CScriptedIconLesson::LessonActionMap.Insert( "get angular distance", LESSON_ACTION_GET_ANGULAR_DISTANCE ); + CScriptedIconLesson::LessonActionMap.Insert( "get player display name", LESSON_ACTION_GET_PLAYER_DISPLAY_NAME ); + CScriptedIconLesson::LessonActionMap.Insert( "classname is", LESSON_ACTION_CLASSNAME_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "modelname is", LESSON_ACTION_MODELNAME_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "team is", LESSON_ACTION_TEAM_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "health less than", LESSON_ACTION_HEALTH_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "health percentage less than", LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN ); + CScriptedIconLesson::LessonActionMap.Insert( "get active weapon", LESSON_ACTION_GET_ACTIVE_WEAPON ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon is", LESSON_ACTION_WEAPON_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon has", LESSON_ACTION_WEAPON_HAS ); + CScriptedIconLesson::LessonActionMap.Insert( "get active weapon slot", LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "get weapon slot", LESSON_ACTION_GET_WEAPON_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "get weapon in slot", LESSON_ACTION_GET_WEAPON_IN_SLOT ); + CScriptedIconLesson::LessonActionMap.Insert( "clip percentage less than", LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo low", LESSON_ACTION_WEAPON_AMMO_LOW ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo full", LESSON_ACTION_WEAPON_AMMO_FULL ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon ammo empty", LESSON_ACTION_WEAPON_AMMO_EMPTY ); + CScriptedIconLesson::LessonActionMap.Insert( "weapon can use", LESSON_ACTION_WEAPON_CAN_USE ); + CScriptedIconLesson::LessonActionMap.Insert( "use target is", LESSON_ACTION_USE_TARGET_IS ); + CScriptedIconLesson::LessonActionMap.Insert( "get use target", LESSON_ACTION_GET_USE_TARGET ); + CScriptedIconLesson::LessonActionMap.Insert( "get potential use target", LESSON_ACTION_GET_POTENTIAL_USE_TARGET ); + + // Add mod actions to the map + //Mod_PreReadLessonsFromFile(); +} diff --git a/game/client/c_baselesson.h b/game/client/c_baselesson.h new file mode 100644 index 000000000..eec3a63a7 --- /dev/null +++ b/game/client/c_baselesson.h @@ -0,0 +1,460 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client handler for instructing players how to play +// +//=====================================================================================// + +#ifndef _C_BASELESSON_H_ +#define _C_BASELESSON_H_ + + +#include "GameEventListener.h" +#include "hud_locator_target.h" + + +#define DECLARE_LESSON( _lessonClassName, _baseLessonClassName ) \ + typedef _baseLessonClassName BaseClass;\ + typedef _lessonClassName ThisClass;\ + _lessonClassName( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity )\ + : _baseLessonClassName( pchName, bIsDefaultHolder, bIsOpenOpportunity )\ + {\ + Init();\ + } + + +enum LessonInstanceType +{ + LESSON_INSTANCE_MULTIPLE, + LESSON_INSTANCE_SINGLE_OPEN, + LESSON_INSTANCE_FIXED_REPLACE, + LESSON_INSTANCE_SINGLE_ACTIVE, + + LESSON_INSTANCE_TYPE_TOTAL +}; + + +// This is used to solve a problem where bots can take the place of a player, where on or the other don't have valid entities on the client at the same time +#define MAX_DELAYED_PLAYER_SWAPS 8 + +struct delayed_player_swap_t +{ + CHandle *phHandleToChange; + int iNewUserID; + + delayed_player_swap_t( void ) + { + phHandleToChange = NULL; + iNewUserID = -1; + } +}; + + +abstract_class CBaseLesson : public CGameEventListener +{ +public: + CBaseLesson( const char *pchName, bool bIsDefaultHolder, bool bIsOpenOpportunity ); + virtual ~CBaseLesson( void ); + + void AddPrerequisite( const char *pchLessonName ); + + const CGameInstructorSymbol& GetNameSymbol( void ) const { return m_stringName; } + const char * GetName( void ) const { return m_stringName.String(); } + int GetPriority( void ) const { return m_iPriority; } + const char * GetCloseReason( void ) const { return m_stringCloseReason.String(); } + void SetCloseReason( const char *pchReason ) { m_stringCloseReason = pchReason; } + + CBaseLesson* GetRoot( void ) const { return m_pRoot; } + void SetRoot( CBaseLesson *pRoot ); + const CUtlVector < CBaseLesson * >* GetChildren( void ) const { return &m_OpenOpportunities; } + + float GetInitTime( void ) { return m_fInitTime; } + void SetStartTime( void ) { m_fStartTime = gpGlobals->curtime; } + void ResetStartTime( void ) { m_fStartTime = 0.0f; m_bHasPlayedSound = false; } + + bool ShouldShowSpew( void ); + bool NoPriority( void ) const; + bool IsDefaultHolder( void ) const { return m_bIsDefaultHolder; } + bool IsOpenOpportunity( void ) const { return m_bIsOpenOpportunity; } + bool IsLocked( void ) const; + bool CanOpenWhenDead( void ) const { return m_bCanOpenWhenDead; } + bool IsInstructing( void ) const { return ( m_fStartTime > 0.0f ); } + bool IsLearned( void ) const; + bool PrerequisitesHaveBeenMet( void ) const; + bool IsTimedOut( void ); + + int InstanceType( void ) const { return m_iInstanceType; } + const CGameInstructorSymbol& GetReplaceKeySymbol( void ) const { return m_stringReplaceKey; } + const char* GetReplaceKey( void ) const { return m_stringReplaceKey.String(); } + int GetFixedInstancesMax( void ) const { return m_iFixedInstancesMax; } + bool ShouldReplaceOnlyWhenStopped( void ) const { return m_bReplaceOnlyWhenStopped; } + void SetInstanceActive( bool bInstanceActive ) { m_bInstanceActive = bInstanceActive; } + bool IsInstanceActive( void ) const { return m_bInstanceActive; } + + void ResetDisplaysAndSuccesses( void ); + bool IncDisplayCount( void ); + bool IncSuccessCount( void ); + void SetDisplayCount( int iDisplayCount ) { m_iDisplayCount = iDisplayCount; } + void SetSuccessCount( int iSuccessCount ) { m_iSuccessCount = iSuccessCount; } + int GetDisplayCount( void ) const { return m_iDisplayCount; } + int GetSuccessCount( void ) const { return m_iSuccessCount; } + int GetDisplayLimit( void ) const { return m_iDisplayLimit; } + int GetSuccessLimit( void ) const { return m_iSuccessLimit; } + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void InitPrerequisites( void ) {}; + virtual void Start( void ) = 0; + virtual void Stop( void ) = 0; + virtual void OnOpen( void ) {}; + virtual void Update( void ) {}; + virtual void UpdateInactive( void ) {}; + + virtual bool ShouldDisplay( void ) const { return true; } + virtual bool IsVisible( void ) const { return true; } + virtual bool WasDisplayed( void ) const { return m_bWasDisplayed ? true : false; } + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ) {} + virtual void TakePlaceOf( CBaseLesson *pLesson ); + + const char *GetGroup() { return m_szLessonGroup.String(); } + void SetEnabled( bool bEnabled ) { m_bDisabled = !bEnabled; } + +protected: + void MarkSucceeded( void ); + void CloseOpportunity( const char *pchReason ); + bool DoDelayedPlayerSwaps( void ) const; + +private: + + CBaseLesson *m_pRoot; + CUtlVector < CBaseLesson * > m_OpenOpportunities; + CUtlVector < const CBaseLesson * > m_Prerequisites; + + CGameInstructorSymbol m_stringCloseReason; + CGameInstructorSymbol m_stringName; + + bool m_bInstanceActive : 1; + bool m_bSuccessCounted : 1; + bool m_bIsDefaultHolder : 1; + bool m_bIsOpenOpportunity : 1; + +protected: + LessonInstanceType m_iInstanceType; + + int m_iPriority; + CGameInstructorSymbol m_stringReplaceKey; + int m_iFixedInstancesMax; + bool m_bReplaceOnlyWhenStopped; + int m_iTeam; + bool m_bOnlyKeyboard; + bool m_bOnlyGamepad; + + int m_iDisplayLimit; + int m_iDisplayCount; + bool m_bWasDisplayed; + int m_iSuccessLimit; + int m_iSuccessCount; + + float m_fLockDuration; + float m_fTimeout; + float m_fInitTime; + float m_fStartTime; + float m_fLockTime; + float m_fUpdateInterval; + bool m_bHasPlayedSound; + + CGameInstructorSymbol m_szStartSound; + CGameInstructorSymbol m_szLessonGroup; + + bool m_bCanOpenWhenDead; + bool m_bBumpWithTimeoutWhenLearned; + bool m_bCanTimeoutWhileInactive; + bool m_bDisabled; + + // Right now we can only queue up 4 swaps... + // this number can be increased if more entity handle scripted variables are added + mutable delayed_player_swap_t m_pDelayedPlayerSwap[ MAX_DELAYED_PLAYER_SWAPS ]; + mutable int m_iNumDelayedPlayerSwaps; + +public: + + // Colors for console spew in verbose mode + static Color m_rgbaVerboseHeader; + static Color m_rgbaVerbosePlain; + static Color m_rgbaVerboseName; + static Color m_rgbaVerboseOpen; + static Color m_rgbaVerboseClose; + static Color m_rgbaVerboseSuccess; + static Color m_rgbaVerboseUpdate; +}; + + +class CTextLesson : public CBaseLesson +{ +public: + DECLARE_LESSON( CTextLesson, CBaseLesson ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + +protected: + + CGameInstructorSymbol m_szDisplayText; + CGameInstructorSymbol m_szDisplayParamText; + CGameInstructorSymbol m_szBinding; + CGameInstructorSymbol m_szGamepadBinding; +}; + + +class CIconLesson : public CTextLesson +{ +public: + DECLARE_LESSON( CIconLesson, CTextLesson ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void Start( void ); + virtual void Stop( void ); + virtual void Update( void ); + virtual void UpdateInactive( void ); + + virtual bool ShouldDisplay( void ) const; + virtual bool IsVisible( void ) const; + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ); + virtual void TakePlaceOf( CBaseLesson *pLesson ); + + void SetLocatorBinding( CLocatorTarget * pLocatorTarget ); + + const char *GetCaptionColorString() { return m_szCaptionColor.String(); } + + bool IsPresentComplete( void ); + void PresentStart( void ); + void PresentEnd( void ); + +private: + virtual void UpdateLocatorTarget( CLocatorTarget *pLocatorTarget, C_BaseEntity *pIconTarget ); + +#ifdef MAPBASE + Vector GetIconTargetPosition( C_BaseEntity *pIconTarget ); +#endif // MAPBASE + +protected: + CHandle m_hIconTarget; + CGameInstructorSymbol m_szVguiTargetName; + CGameInstructorSymbol m_szVguiTargetLookup; + int m_nVguiTargetEdge; + float m_flUpOffset; + float m_flRelativeUpOffset; + float m_fFixedPositionX; + float m_fFixedPositionY; + + int m_hLocatorTarget; + int m_iFlags; + + float m_fRange; + float m_fCurrentDistance; + float m_fOnScreenStartTime; + float m_fUpdateDistanceTime; + + CGameInstructorSymbol m_szOnscreenIcon; + CGameInstructorSymbol m_szOffscreenIcon; + CGameInstructorSymbol m_szCaptionColor; + + bool m_bFixedPosition; + bool m_bNoIconTarget; + bool m_bAllowNodrawTarget; + bool m_bVisible; + bool m_bShowWhenOccluded; + bool m_bNoOffscreen; + bool m_bForceCaption; + +#ifdef MAPBASE + int m_iIconTargetPos; + + enum + { + ICON_TARGET_EYE_POSITION, + ICON_TARGET_ORIGIN, + ICON_TARGET_CENTER, + }; + + CGameInstructorSymbol m_szHudHint; +#endif // MAPBASE +}; + +enum LessonAction +{ + LESSON_ACTION_NONE, + + LESSON_ACTION_SCOPE_IN, + LESSON_ACTION_SCOPE_OUT, + LESSON_ACTION_CLOSE, + LESSON_ACTION_SUCCESS, + LESSON_ACTION_LOCK, + LESSON_ACTION_PRESENT_COMPLETE, + LESSON_ACTION_PRESENT_START, + LESSON_ACTION_PRESENT_END, + + LESSON_ACTION_REFERENCE_OPEN, + + LESSON_ACTION_SET, + LESSON_ACTION_ADD, + LESSON_ACTION_SUBTRACT, + LESSON_ACTION_MULTIPLY, + LESSON_ACTION_IS, + LESSON_ACTION_LESS_THAN, + LESSON_ACTION_HAS_PREFIX, + LESSON_ACTION_HAS_BIT, + LESSON_ACTION_BIT_COUNT_IS, + LESSON_ACTION_BIT_COUNT_LESS_THAN, + + LESSON_ACTION_GET_DISTANCE, + LESSON_ACTION_GET_ANGULAR_DISTANCE, + LESSON_ACTION_GET_PLAYER_DISPLAY_NAME, + LESSON_ACTION_CLASSNAME_IS, + LESSON_ACTION_MODELNAME_IS, + LESSON_ACTION_TEAM_IS, + LESSON_ACTION_HEALTH_LESS_THAN, + LESSON_ACTION_HEALTH_PERCENTAGE_LESS_THAN, + LESSON_ACTION_GET_ACTIVE_WEAPON, + LESSON_ACTION_WEAPON_IS, + LESSON_ACTION_WEAPON_HAS, + LESSON_ACTION_GET_ACTIVE_WEAPON_SLOT, + LESSON_ACTION_GET_WEAPON_SLOT, + LESSON_ACTION_GET_WEAPON_IN_SLOT, + LESSON_ACTION_CLIP_PERCENTAGE_LESS_THAN, + LESSON_ACTION_WEAPON_AMMO_LOW, + LESSON_ACTION_WEAPON_AMMO_FULL, + LESSON_ACTION_WEAPON_AMMO_EMPTY, + LESSON_ACTION_WEAPON_CAN_USE, + LESSON_ACTION_USE_TARGET_IS, + LESSON_ACTION_GET_USE_TARGET, + LESSON_ACTION_GET_POTENTIAL_USE_TARGET, + + // Enum continued in Mod_LessonAction + LESSON_ACTION_MOD_START, +}; + +struct LessonElement_t +{ + int iVariable; + int iParamVarIndex; + int iAction; + _fieldtypes paramType; + CGameInstructorSymbol szParam; + bool bNot : 1; + bool bOptionalParam : 1; + + LessonElement_t( int p_iVariable, int p_iAction, bool p_bNot, bool p_bOptionalParam, const char *pchParam, int p_iParamVarIndex, _fieldtypes p_paramType ) + { + iVariable = p_iVariable; + iAction = p_iAction; + bNot = p_bNot; + bOptionalParam = p_bOptionalParam; + szParam = pchParam; + iParamVarIndex = p_iParamVarIndex; + paramType = p_paramType; + } + + LessonElement_t( const LessonElement_t &p_LessonElement ) + { + iVariable = p_LessonElement.iVariable; + iAction = p_LessonElement.iAction; + bNot = p_LessonElement.bNot; + bOptionalParam = p_LessonElement.bOptionalParam; + szParam = p_LessonElement.szParam; + iParamVarIndex = p_LessonElement.iParamVarIndex; + paramType = p_LessonElement.paramType; + } +}; + +struct LessonEvent_t +{ + CUtlVector< LessonElement_t > elements; + CGameInstructorSymbol szEventName; +}; + +class CScriptedIconLesson : public CIconLesson +{ +public: + DECLARE_LESSON( CScriptedIconLesson, CIconLesson ) + + virtual ~CScriptedIconLesson( void ); + + static void PreReadLessonsFromFile( void ); + static void Mod_PreReadLessonsFromFile( void ); + + void Init( void ); // NOT virtual, each constructor calls their own + virtual void InitPrerequisites( void ); + virtual void OnOpen( void ); + virtual void Update( void ); + + virtual void SwapOutPlayers( int iOldUserID, int iNewUserID ); + + virtual void FireGameEvent( IGameEvent *event ); + virtual void ProcessOpenGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + virtual void ProcessCloseGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + virtual void ProcessSuccessGameEvents( const CScriptedIconLesson *pRootLesson, const char *name, IGameEvent *event ); + + CUtlVector< LessonEvent_t >& GetOpenEvents( void ) { return m_OpenEvents; } + CUtlVector< LessonEvent_t >& GetCloseEvents( void ) { return m_CloseEvents; } + CUtlVector< LessonEvent_t >& GetSuccessEvents( void ) { return m_SuccessEvents; } + CUtlVector< LessonEvent_t >& GetOnOpenEvents( void ) { return m_OnOpenEvents; } + CUtlVector< LessonEvent_t >& GetUpdateEvents( void ) { return m_UpdateEvents; } + + bool ProcessElements( IGameEvent *event, const CUtlVector< LessonElement_t > *pElements ); + +private: + void InitElementsFromKeys( CUtlVector< LessonElement_t > *pLessonElements, KeyValues *pKey ); + void InitElementsFromElements( CUtlVector< LessonElement_t > *pLessonElements, const CUtlVector< LessonElement_t > *pLessonElements2 ); + + void InitFromKeys( KeyValues *pKey ); + + bool ProcessElement( IGameEvent *event, const LessonElement_t *pLessonElement, bool bInFailedScope ); + + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, float &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, int &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, bool &bVar, const CGameInstructorSymbol *pchParamName, float fParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam ); + bool ProcessElementAction( int iAction, bool bNot, const char *pchVarName, CGameInstructorSymbol *pchVar, const CGameInstructorSymbol *pchParamName, const char *pchParam ); + + // Implemented per mod so they can have custom actions + bool Mod_ProcessElementAction( int iAction, bool bNot, const char *pchVarName, EHANDLE &hVar, const CGameInstructorSymbol *pchParamName, float fParam, C_BaseEntity *pParam, const char *pchParam, bool &bModHandled ); + + LessonEvent_t * AddOpenEvent( void ); + LessonEvent_t * AddCloseEvent( void ); + LessonEvent_t * AddSuccessEvent( void ); + LessonEvent_t * AddOnOpenEvent( void ); + LessonEvent_t * AddUpdateEvent( void ); + +private: + static CUtlDict< int, int > LessonActionMap; + + EHANDLE m_hLocalPlayer; + float m_fOutput; + CHandle m_hEntity1; + CHandle m_hEntity2; + CGameInstructorSymbol m_szString1; + CGameInstructorSymbol m_szString2; + int m_iInteger1; + int m_iInteger2; + float m_fFloat1; + float m_fFloat2; + + CUtlVector< CGameInstructorSymbol > m_PrerequisiteNames; + CUtlVector< LessonEvent_t > m_OpenEvents; + CUtlVector< LessonEvent_t > m_CloseEvents; + CUtlVector< LessonEvent_t > m_SuccessEvents; + CUtlVector< LessonEvent_t > m_OnOpenEvents; + CUtlVector< LessonEvent_t > m_UpdateEvents; + + float m_fUpdateEventTime; + CScriptedIconLesson *m_pDefaultHolder; + + int m_iScopeDepth; + + // Need this to get offsets to scripted variables + friend class LessonVariableInfo; + friend int LessonActionFromString( const char *pchName ); +}; + + +#endif // _C_BASELESSON_H_ diff --git a/game/client/c_baseplayer.cpp b/game/client/c_baseplayer.cpp index 9b3b8c8c0..d02b53604 100644 --- a/game/client/c_baseplayer.cpp +++ b/game/client/c_baseplayer.cpp @@ -50,6 +50,10 @@ #include "sourcevr/isourcevirtualreality.h" #include "client_virtualreality.h" +#ifdef MAPBASE +#include "viewrender.h" +#endif // MAPBASE + #if defined USES_ECON_ITEMS #include "econ_wearable.h" #endif @@ -129,6 +133,16 @@ void RecvProxy_LocalVelocityZ( const CRecvProxyData *pData, void *pStruct, void void RecvProxy_ObserverTarget( const CRecvProxyData *pData, void *pStruct, void *pOut ); void RecvProxy_ObserverMode ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +#ifdef MAPBASE +// Needs to shift bits back +void RecvProxy_ShiftPlayerSpawnflags( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + C_BasePlayer *pPlayer = (C_BasePlayer *)pStruct; + + pPlayer->m_spawnflags = (pData->m_Value.m_Int) << 16; +} +#endif // MAPBASE + // -------------------------------------------------------------------------------- // // RecvTable for CPlayerState. // -------------------------------------------------------------------------------- // @@ -176,6 +190,11 @@ BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local ) // 3d skybox data RecvPropInt(RECVINFO(m_skybox3d.scale)), RecvPropVector(RECVINFO(m_skybox3d.origin)), +#ifdef MAPBASE + RecvPropVector(RECVINFO(m_skybox3d.angles)), + RecvPropEHandle(RECVINFO(m_skybox3d.skycamera)), + RecvPropInt( RECVINFO( m_skybox3d.skycolor ), 0, RecvProxy_IntToColor32 ), +#endif // MAPBASE RecvPropInt(RECVINFO(m_skybox3d.area)), // 3d skybox fog data @@ -187,6 +206,9 @@ BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local ) RecvPropFloat( RECVINFO( m_skybox3d.fog.start ) ), RecvPropFloat( RECVINFO( m_skybox3d.fog.end ) ), RecvPropFloat( RECVINFO( m_skybox3d.fog.maxdensity ) ), +#ifdef MAPBASE + RecvPropFloat( RECVINFO( m_skybox3d.fog.farz ) ), +#endif // MAPBASE // fog data RecvPropEHandle( RECVINFO( m_PlayerFog.m_hCtrl ) ), @@ -203,6 +225,14 @@ BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local ) RecvPropInt( RECVINFO( m_audio.soundscapeIndex ) ), RecvPropInt( RECVINFO( m_audio.localBits ) ), RecvPropEHandle( RECVINFO( m_audio.ent ) ), + + //Tony; tonemap stuff! -- TODO! Optimize this with bit sizes from env_tonemap_controller. + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flTonemapScale ) ), + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flTonemapRate ) ), + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flBloomScale ) ), + + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flAutoExposureMin ) ), + RecvPropFloat ( RECVINFO( m_TonemapParams.m_flAutoExposureMax ) ), END_RECV_TABLE() // -------------------------------------------------------------------------------- // @@ -245,6 +275,15 @@ END_RECV_TABLE() RecvPropInt ( RECVINFO( m_nWaterLevel ) ), RecvPropFloat ( RECVINFO( m_flLaggedMovementValue )), +#ifdef MAPBASE + // Transmitted from the server for internal player spawnflags. + // See baseplayer_shared.h for more details. + RecvPropInt ( RECVINFO( m_spawnflags ), 0, RecvProxy_ShiftPlayerSpawnflags ), + + RecvPropBool ( RECVINFO( m_bDrawPlayerModelExternally ) ), + RecvPropBool ( RECVINFO( m_bInTriggerFall ) ), +#endif // MAPBASE + END_RECV_TABLE() @@ -293,6 +332,11 @@ END_RECV_TABLE() RecvPropString( RECVINFO(m_szLastPlaceName) ), +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropEHandle( RECVINFO( m_hPostProcessCtrl ) ), // Send to everybody - for spectating + RecvPropEHandle( RECVINFO( m_hColorCorrectionCtrl ) ), // Send to everybody - for spectating +#endif // MAPBASE + #if defined USES_ECON_ITEMS RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ), #endif @@ -396,7 +440,10 @@ BEGIN_PREDICTION_DATA( C_BasePlayer ) END_PREDICTION_DATA() +// link this in each derived player class, like the server!! +#if 0 LINK_ENTITY_TO_CLASS( player, C_BasePlayer ); +#endif // -------------------------------------------------------------------------------- // // Functions. @@ -451,6 +498,13 @@ C_BasePlayer::~C_BasePlayer() s_pLocalPlayer = NULL; } +#ifdef MAPBASE_VSCRIPT + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } +#endif // MAPBASE_VSCRIPT + delete m_pFlashlight; } @@ -805,6 +859,14 @@ void C_BasePlayer::PostDataUpdate( DataUpdateType_t updateType ) // changed level, which would cause the snd_soundmixer to be left modified. ConVar *pVar = (ConVar *)cvar->FindVar( "snd_soundmixer" ); pVar->Revert(); + +#ifdef MAPBASE_VSCRIPT + // Moved here from LevelInitPostEntity, which is executed before local player is spawned. + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } +#endif // MAPBASE_VSCRIPT } } @@ -1054,6 +1116,16 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // If we're in vgui mode *and* we're holding down mouse buttons, // stay in vgui mode even if we're outside the screen bounds +#ifdef VGUI_SCREEN_FIX + if (m_pCurrentVguiScreen.Get() && (pCmd->buttons & (IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT))) + { + SetVGuiScreenButtonState( m_pCurrentVguiScreen.Get(), pCmd->buttons ); + + // Kill all attack inputs if we're in vgui screen mode + pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT); + return; + } +#else if (m_pCurrentVguiScreen.Get() && (pCmd->buttons & (IN_ATTACK | IN_ATTACK2)) ) { SetVGuiScreenButtonState( m_pCurrentVguiScreen.Get(), pCmd->buttons ); @@ -1062,6 +1134,7 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); return; } +#endif // VGUI_SCREEN_FIX // We're not in vgui input mode if we're moving, or have hit a key // that will make us move... @@ -1279,6 +1352,10 @@ void C_BasePlayer::AddEntity( void ) // Add in lighting effects CreateLightEffects(); + +#ifdef MAPBASE + SetLocalAnglesDim( X_INDEX, 0 ); +#endif // MAPBASE } extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); @@ -1385,11 +1462,35 @@ bool C_BasePlayer::ShouldInterpolate() bool C_BasePlayer::ShouldDraw() { +#ifdef MAPBASE + // We have to "always draw" a player with m_bDrawPlayerModelExternally in order to show up in whatever rendering list all of the views use, + // but we can't put this in ShouldDrawThisPlayer() because we would have no way of knowing if it stomps the other checks that draw the player model anyway. + // As a result, we have to put it here in the central ShouldDraw() function. DrawModel() makes sure we only draw in non-main views and nothing's drawing the model anyway. + return (ShouldDrawThisPlayer() || m_bDrawPlayerModelExternally) && BaseClass::ShouldDraw(); +#else return ShouldDrawThisPlayer() && BaseClass::ShouldDraw(); +#endif // MAPBASE } int C_BasePlayer::DrawModel( int flags ) { +#ifdef MAPBASE + if (m_bDrawPlayerModelExternally) + { + // Draw the player in any view except the main or "intro" view, both of which are default first-person views. + // HACKHACK: Also don't draw in shadow depth textures if the player's flashlight is on, as that causes the playermodel to block it. + view_id_t viewID = CurrentViewID(); + if (viewID == VIEW_MAIN || viewID == VIEW_INTRO_CAMERA || (viewID == VIEW_SHADOW_DEPTH_TEXTURE && IsEffectActive(EF_DIMLIGHT))) + { + // Make sure the player model wouldn't draw anyway... + if (!ShouldDrawThisPlayer()) + return 0; + } + + return BaseClass::DrawModel( flags ); + } +#endif // MAPBASE + #ifndef PORTAL // In Portal this check is already performed as part of // C_Portal_Player::DrawModel() @@ -1398,9 +1499,42 @@ int C_BasePlayer::DrawModel( int flags ) return 0; } #endif + return BaseClass::DrawModel( flags ); } +#ifdef MAPBASE +ConVar cl_player_allow_thirdperson_projtex( "cl_player_allow_thirdperson_projtex", "1", FCVAR_NONE, "Allows players to receive projected textures if they're non-local or in third person." ); +ConVar cl_player_allow_thirdperson_rttshadows( "cl_player_allow_thirdperson_rttshadows", "0", FCVAR_NONE, "Allows players to cast RTT shadows if they're non-local or in third person." ); +ConVar cl_player_allow_firstperson_projtex( "cl_player_allow_firstperson_projtex", "1", FCVAR_NONE, "Allows players to receive projected textures even if they're in first person." ); +ConVar cl_player_allow_firstperson_rttshadows( "cl_player_allow_firstperson_rttshadows", "0", FCVAR_NONE, "Allows players to cast RTT shadows even if they're in first person." ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ShadowType_t C_BasePlayer::ShadowCastType() +{ + if ( (!IsLocalPlayer() || ShouldDraw()) ? !cl_player_allow_thirdperson_rttshadows.GetBool() : !cl_player_allow_firstperson_rttshadows.GetBool() ) + return SHADOWS_NONE; + + if ( !IsVisible() ) + return SHADOWS_NONE; + + return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC; +} + +//----------------------------------------------------------------------------- +// Should this object receive shadows? +//----------------------------------------------------------------------------- +bool C_BasePlayer::ShouldReceiveProjectedTextures( int flags ) +{ + if ( (!IsLocalPlayer() || ShouldDraw()) ? !cl_player_allow_thirdperson_projtex.GetBool() : !cl_player_allow_firstperson_projtex.GetBool() ) + return false; + + return BaseClass::ShouldReceiveProjectedTextures( flags ); +} +#endif // MAPBASE + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1522,7 +1656,14 @@ void C_BasePlayer::CalcChaseCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& } } - if ( target && !target->IsPlayer() && target->IsNextBot() ) + // SDK TODO + if ( target && target->IsBaseTrain() ) + { + // if this is a train, we want to be back a little further so we can see more of it + flMaxDistance *= 2.5f; + m_flObserverChaseDistance = flMaxDistance; + } + else if ( target && !target->IsPlayer() && target->IsNextBot() ) { // if this is a boss, we want to be back a little further so we can see more of it flMaxDistance *= 2.5f; @@ -1855,6 +1996,12 @@ void C_BasePlayer::ThirdPersonSwitch( bool bThirdperson ) } } } + else + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ( pWeapon ) + pWeapon->ThirdPersonSwitch( bThirdperson ); + } } @@ -2796,8 +2943,26 @@ void C_BasePlayer::UpdateFogBlend( void ) } } +#ifdef MAPBASE // From Alien Swarm SDK //----------------------------------------------------------------------------- -// Purpose: +// Purpose: Getter for m_hPostProcessCtrl +//----------------------------------------------------------------------------- +C_PostProcessController* C_BasePlayer::GetActivePostProcessController() const +{ + return m_hPostProcessCtrl.Get(); +} + +//----------------------------------------------------------------------------- +// Purpose: Getter for m_hColorCorrectionCtrl +//----------------------------------------------------------------------------- +C_ColorCorrection* C_BasePlayer::GetActiveColorCorrection() const +{ + return m_hColorCorrectionCtrl.Get(); +} +#endif // MAPBASE + +//----------------------------------------------------------------------------- +// Purpose: Retrieve the player's Steam user ID //----------------------------------------------------------------------------- bool C_BasePlayer::GetSteamID( CSteamID *pID ) { diff --git a/game/client/c_baseplayer.h b/game/client/c_baseplayer.h index 9d3657bf6..c083aa3c8 100644 --- a/game/client/c_baseplayer.h +++ b/game/client/c_baseplayer.h @@ -23,6 +23,10 @@ #include "hintsystem.h" #include "SoundEmitterSystem/isoundemittersystembase.h" #include "c_env_fog_controller.h" +#ifdef MAPBASE // From Alien Swarm SDK +#include "c_postprocesscontroller.h" +#include "c_colorcorrection.h" +#endif // MAPBASE #include "igameevents.h" #include "GameEventListener.h" @@ -37,6 +41,7 @@ class C_BaseViewModel; class C_FuncLadder; class CFlashlightEffect; class C_EconWearable; +class C_PostProcessController; extern int g_nKillCamMode; extern int g_nKillCamTarget1; @@ -182,8 +187,8 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener virtual void TeamChange( int iNewTeam ); // Flashlight - void Flashlight( void ); - void UpdateFlashlight( void ); + void Flashlight( void ); + virtual void UpdateFlashlight( void ); // Weapon selection code virtual bool IsAllowedToSwitchWeapons( void ) { return !IsObserver(); } @@ -202,6 +207,11 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener void SetMaxSpeed( float flMaxSpeed ) { m_flMaxspeed = flMaxSpeed; } float MaxSpeed() const { return m_flMaxspeed; } +#ifdef MAPBASE + // See c_baseplayer.cpp + virtual ShadowType_t ShadowCastType(); + virtual bool ShouldReceiveProjectedTextures( int flags ); +#else // Should this object cast shadows? virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; } @@ -209,7 +219,7 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener { return false; } - +#endif // MAPBASE bool IsLocalPlayer( void ) const; @@ -379,6 +389,11 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener void UpdateFogController( void ); void UpdateFogBlend( void ); +#ifdef MAPBASE // From Alien Swarm SDK + C_PostProcessController* GetActivePostProcessController() const; + C_ColorCorrection* GetActiveColorCorrection() const; +#endif // MAPBASE + float GetFOVTime( void ){ return m_flFOVTime; } virtual void OnAchievementAchieved( int iAchievement ) {} @@ -443,20 +458,36 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener float m_flConstraintWidth; float m_flConstraintSpeedFactor; +#ifdef MAPBASE + // Transmitted from the server for internal player spawnflags. + // See baseplayer_shared.h for more details. + int m_spawnflags; + + inline bool HasSpawnFlags( int flags ) { return (m_spawnflags & flags) != 0; } + inline void RemoveSpawnFlags( int flags ) { m_spawnflags &= ~flags; } + inline void AddSpawnFlags( int flags ) { m_spawnflags |= flags; } + + // Allows the player's model to draw on non-main views, like monitors or mirrors. + bool m_bDrawPlayerModelExternally; + + bool m_bInTriggerFall; +#endif // MAPBASE + protected: - void CalcPlayerView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); - void CalcVehicleView(IClientVehicle *pVehicle, Vector& eyeOrigin, QAngle& eyeAngles, + //Tony; made all of these virtual so mods can override. + virtual void CalcPlayerView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); + virtual void CalcVehicleView(IClientVehicle *pVehicle, Vector& eyeOrigin, QAngle& eyeAngles, float& zNear, float& zFar, float& fov ); float& zNear, float& zFar, float& fov ); virtual void CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); virtual Vector GetChaseCamViewOffset( CBaseEntity *target ); - void CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); + virtual void CalcChaseCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); virtual void CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); virtual float GetDeathCamInterpolationTime(); virtual void CalcDeathCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); - void CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov); + virtual void CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov); virtual void CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ); // Check to see if we're in vgui input mode... @@ -628,6 +659,11 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener // One for left and one for right side of step StepSoundCache_t m_StepSoundCache[ 2 ]; +#ifdef MAPBASE // From Alien Swarm SDK + CNetworkHandle( C_PostProcessController, m_hPostProcessCtrl ); // active postprocessing controller + CNetworkHandle( C_ColorCorrection, m_hColorCorrectionCtrl ); // active FXVolume color correction +#endif // MAPBASE + public: const char *GetLastKnownPlaceName( void ) const { return m_szLastPlaceName; } // return the last nav place name the player occupied diff --git a/game/client/c_baseviewmodel.cpp b/game/client/c_baseviewmodel.cpp index 98132bc95..be05021c0 100644 --- a/game/client/c_baseviewmodel.cpp +++ b/game/client/c_baseviewmodel.cpp @@ -32,9 +32,9 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#ifdef CSTRIKE_DLL +#if defined(CSTRIKE_DLL) || defined(MAPBASE) ConVar cl_righthand( "cl_righthand", "1", FCVAR_ARCHIVE, "Use right-handed view models." ); -#endif +#endif // CSTRIKE_DLL || MAPBASE #ifdef TF_CLIENT_DLL ConVar cl_flipviewmodels( "cl_flipviewmodels", "0", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_NOT_CONNECTED, "Flip view models." ); @@ -192,9 +192,9 @@ bool C_BaseViewModel::Interpolate( float currentTime ) } -inline bool C_BaseViewModel::ShouldFlipViewModel() +bool C_BaseViewModel::ShouldFlipViewModel() { -#ifdef CSTRIKE_DLL +#if defined(CSTRIKE_DLL) || defined(MAPBASE) // If cl_righthand is set, then we want them all right-handed. CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); if ( pWeapon ) @@ -202,7 +202,7 @@ inline bool C_BaseViewModel::ShouldFlipViewModel() const FileWeaponInfo_t *pInfo = &pWeapon->GetWpnData(); return pInfo->m_bAllowFlipping && pInfo->m_bBuiltRightHanded != cl_righthand.GetBool(); } -#endif +#endif // CSTRIKE_DLL || MAPBASE #ifdef TF_CLIENT_DLL CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); diff --git a/game/client/c_colorcorrection.cpp b/game/client/c_colorcorrection.cpp index 6960031d5..3aefc1e66 100644 --- a/game/client/c_colorcorrection.cpp +++ b/game/client/c_colorcorrection.cpp @@ -6,6 +6,7 @@ //===========================================================================// #include "cbase.h" +#include "c_colorcorrection.h" #include "filesystem.h" #include "cdll_client_int.h" #include "colorcorrectionmgr.h" @@ -17,37 +18,9 @@ static ConVar mat_colcorrection_disableentities( "mat_colcorrection_disableentities", "0", FCVAR_NONE, "Disable map color-correction entities" ); - -//------------------------------------------------------------------------------ -// Purpose : Color correction entity with radial falloff -//------------------------------------------------------------------------------ -class C_ColorCorrection : public C_BaseEntity -{ -public: - DECLARE_CLASS( C_ColorCorrection, C_BaseEntity ); - - DECLARE_CLIENTCLASS(); - - C_ColorCorrection(); - virtual ~C_ColorCorrection(); - - void OnDataChanged(DataUpdateType_t updateType); - bool ShouldDraw(); - - void ClientThink(); - -private: - Vector m_vecOrigin; - - float m_minFalloff; - float m_maxFalloff; - float m_flCurWeight; - char m_netLookupFilename[MAX_PATH]; - - bool m_bEnabled; - - ClientCCHandle_t m_CCHandle; -}; +#ifdef MAPBASE // From Alien Swarm SDK +static ConVar mat_colcorrection_forceentitiesclientside( "mat_colcorrection_forceentitiesclientside", "0", FCVAR_CHEAT, "Forces color correction entities to be updated on the client" ); +#endif // MAPBASE IMPLEMENT_CLIENTCLASS_DT(C_ColorCorrection, DT_ColorCorrection, CColorCorrection) RecvPropVector( RECVINFO(m_vecOrigin) ), @@ -55,7 +28,17 @@ IMPLEMENT_CLIENTCLASS_DT(C_ColorCorrection, DT_ColorCorrection, CColorCorrection RecvPropFloat( RECVINFO(m_maxFalloff) ), RecvPropFloat( RECVINFO(m_flCurWeight) ), RecvPropString( RECVINFO(m_netLookupFilename) ), +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropFloat( RECVINFO(m_flMaxWeight) ), + RecvPropFloat( RECVINFO(m_flFadeInDuration) ), + RecvPropFloat( RECVINFO(m_flFadeOutDuration) ), +#endif // MAPBASE RecvPropBool( RECVINFO(m_bEnabled) ), +#ifdef MAPBASE // From Alien Swarm SDK + RecvPropBool( RECVINFO(m_bMaster) ), + RecvPropBool( RECVINFO(m_bClientSide) ), + RecvPropBool( RECVINFO(m_bExclusive) ) +#endif // MAPBASE END_RECV_TABLE() @@ -65,14 +48,40 @@ END_RECV_TABLE() //------------------------------------------------------------------------------ C_ColorCorrection::C_ColorCorrection() { +#ifdef MAPBASE // From Alien Swarm SDK + m_minFalloff = -1.0f; + m_maxFalloff = -1.0f; + m_flFadeInDuration = 0.0f; + m_flFadeOutDuration = 0.0f; + m_flCurWeight = 0.0f; + m_flMaxWeight = 1.0f; + m_netLookupFilename[0] = '\0'; + m_bEnabled = false; + m_bMaster = false; + m_bExclusive = false; + m_bFadingIn = false; + m_flFadeStartWeight = 0.0f; + m_flFadeStartTime = 0.0f; + m_flFadeDuration = 0.0f; +#endif // MAPBASE m_CCHandle = INVALID_CLIENT_CCHANDLE; } C_ColorCorrection::~C_ColorCorrection() { +#ifdef MAPBASE // From Alien Swarm SDK + g_pColorCorrectionMgr->RemoveColorCorrectionEntity( this, m_CCHandle ); +#else g_pColorCorrectionMgr->RemoveColorCorrection( m_CCHandle ); +#endif // MAPBASE } +#ifdef MAPBASE // From Alien Swarm SDK +bool C_ColorCorrection::IsClientSide() const +{ + return m_bClientSide || mat_colcorrection_forceentitiesclientside.GetBool(); +} +#endif // MAPBASE //------------------------------------------------------------------------------ // Purpose : @@ -87,11 +96,21 @@ void C_ColorCorrection::OnDataChanged(DataUpdateType_t updateType) { if ( m_CCHandle == INVALID_CLIENT_CCHANDLE ) { +#ifdef MAPBASE // From Alien Swarm SDK + // forming a unique name without extension + char cleanName[MAX_PATH]; + V_StripExtension( m_netLookupFilename, cleanName, sizeof( cleanName ) ); + char name[MAX_PATH]; + Q_snprintf( name, MAX_PATH, "%s_%d", cleanName, entindex() ); + + m_CCHandle = g_pColorCorrectionMgr->AddColorCorrectionEntity( this, name, m_netLookupFilename ); +#else char filename[MAX_PATH]; Q_strncpy( filename, m_netLookupFilename, MAX_PATH ); m_CCHandle = g_pColorCorrectionMgr->AddColorCorrection( filename ); SetNextClientThink( ( m_CCHandle != INVALID_CLIENT_CCHANDLE ) ? CLIENT_THINK_ALWAYS : CLIENT_THINK_NEVER ); +#endif // MAPBASE } } } @@ -104,27 +123,29 @@ bool C_ColorCorrection::ShouldDraw() return false; } -void C_ColorCorrection::ClientThink() +#ifdef MAPBASE // From Alien Swarm SDK +void C_ColorCorrection::Update( C_BasePlayer *pPlayer, float ccScale ) { - if ( m_CCHandle == INVALID_CLIENT_CCHANDLE ) - return; + Assert( m_CCHandle != INVALID_CLIENT_CCHANDLE ); if ( mat_colcorrection_disableentities.GetInt() ) { // Allow the colorcorrectionui panel (or user) to turn off color-correction entities - g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f ); + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f, m_bExclusive ); return; } - if( !m_bEnabled && m_flCurWeight == 0.0f ) + // fade weight on client + if ( IsClientSide() ) { - g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f ); - return; + m_flCurWeight = Lerp( GetFadeRatio(), m_flFadeStartWeight, m_bFadingIn ? m_flMaxWeight : 0.0f ); } - C_BaseEntity *pPlayer = C_BasePlayer::GetLocalPlayer(); - if( !pPlayer ) + if( !m_bEnabled && m_flCurWeight == 0.0f ) + { + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f, m_bExclusive ); return; + } Vector playerOrigin = pPlayer->GetAbsOrigin(); @@ -136,21 +157,130 @@ void C_ColorCorrection::ClientThink() if ( weight<0.0f ) weight = 0.0f; if ( weight>1.0f ) weight = 1.0f; } - - g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_flCurWeight * ( 1.0 - weight ) ); - BaseClass::ClientThink(); + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_flCurWeight * ( 1.0 - weight ) * ccScale, m_bExclusive ); +} + +void C_ColorCorrection::EnableOnClient( bool bEnable, bool bSkipFade ) +{ + if ( !IsClientSide() ) + { + return; + } + + m_bFadingIn = bEnable; + + // initialize countdown timer + m_flFadeStartWeight = m_flCurWeight; + float flFadeTimeScale = 1.0f; + if ( m_flMaxWeight != 0.0f ) + { + flFadeTimeScale = m_flCurWeight / m_flMaxWeight; + } + + if ( m_bFadingIn ) + { + flFadeTimeScale = 1.0f - flFadeTimeScale; + } + + if ( bSkipFade ) + { + flFadeTimeScale = 0.0f; + } + + StartFade( flFadeTimeScale * ( m_bFadingIn ? m_flFadeInDuration : m_flFadeOutDuration ) ); + + // update the clientside weight once here, in case the fade duration is 0 + m_flCurWeight = Lerp( GetFadeRatio(), m_flFadeStartWeight, m_bFadingIn ? m_flMaxWeight : 0.0f ); +} + +Vector C_ColorCorrection::GetOrigin() +{ + return m_vecOrigin; } +float C_ColorCorrection::GetMinFalloff() +{ + return m_minFalloff; +} +float C_ColorCorrection::GetMaxFalloff() +{ + return m_maxFalloff; +} +void C_ColorCorrection::SetWeight( float fWeight ) +{ + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, fWeight, false ); +} +void C_ColorCorrection::StartFade( float flDuration ) +{ + m_flFadeStartTime = gpGlobals->curtime; + m_flFadeDuration = MAX( flDuration, 0.0f ); +} +float C_ColorCorrection::GetFadeRatio() const +{ + float flRatio = 1.0f; + if ( m_flFadeDuration != 0.0f ) + { + flRatio = ( gpGlobals->curtime - m_flFadeStartTime ) / m_flFadeDuration; + flRatio = clamp( flRatio, 0.0f, 1.0f ); + } + return flRatio; +} +bool C_ColorCorrection::IsFadeTimeElapsed() const +{ + return ( ( gpGlobals->curtime - m_flFadeStartTime ) > m_flFadeDuration ) || + ( ( gpGlobals->curtime - m_flFadeStartTime ) < 0.0f ); +} + +void UpdateColorCorrectionEntities( C_BasePlayer *pPlayer, float ccScale, C_ColorCorrection **pList, int listCount ) +{ + for ( int i = 0; i < listCount; i++ ) + { + pList[i]->Update(pPlayer, ccScale); + } +} +#else +void C_ColorCorrection::ClientThink() +{ + if ( m_CCHandle == INVALID_CLIENT_CCHANDLE ) + return; + if ( mat_colcorrection_disableentities.GetInt() ) + { + // Allow the colorcorrectionui panel (or user) to turn off color-correction entities + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f ); + return; + } + if( !m_bEnabled && m_flCurWeight == 0.0f ) + { + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, 0.0f ); + return; + } + C_BaseEntity *pPlayer = C_BasePlayer::GetLocalPlayer(); + if( !pPlayer ) + return; + Vector playerOrigin = pPlayer->GetAbsOrigin(); + float weight = 0; + if ( ( m_minFalloff != -1 ) && ( m_maxFalloff != -1 ) && m_minFalloff != m_maxFalloff ) + { + float dist = (playerOrigin - m_vecOrigin).Length(); + weight = (dist-m_minFalloff) / (m_maxFalloff-m_minFalloff); + if ( weight<0.0f ) weight = 0.0f; + if ( weight>1.0f ) weight = 1.0f; + } + + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCHandle, m_flCurWeight * ( 1.0 - weight ) ); + BaseClass::ClientThink(); +} +#endif // MAPBASE diff --git a/game/client/c_colorcorrection.h b/game/client/c_colorcorrection.h new file mode 100644 index 000000000..670116eb6 --- /dev/null +++ b/game/client/c_colorcorrection.h @@ -0,0 +1,88 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Note that this header exists in the Alien Swarm SDK, but not in stock Source SDK 2013. +// Although technically a new Mapbase file, it only serves to move otherwise identical code, +// so most code and repo conventions will pretend it was always there. +// +// -------------------------------------------------------------------- +// +// Purpose: Color correction entity with simple radial falloff +// +//==================================================================================// + +#ifndef C_COLORCORRECTION_H +#define C_COLORCORRECTION_H +#ifdef _WIN32 +#pragma once +#endif + +#include "colorcorrectionmgr.h" + +//------------------------------------------------------------------------------ +// Purpose : Color correction entity with radial falloff +//------------------------------------------------------------------------------ +class C_ColorCorrection : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_ColorCorrection, C_BaseEntity ); + + DECLARE_CLIENTCLASS(); + + C_ColorCorrection(); + virtual ~C_ColorCorrection(); + + void OnDataChanged(DataUpdateType_t updateType); + bool ShouldDraw(); + +#ifdef MAPBASE // From Alien Swarm SDK + virtual void Update(C_BasePlayer *pPlayer, float ccScale); + + bool IsMaster() const { return m_bMaster; } + bool IsClientSide() const; + bool IsExclusive() const { return m_bExclusive; } + + void EnableOnClient( bool bEnable, bool bSkipFade = false ); + + Vector GetOrigin(); + float GetMinFalloff(); + float GetMaxFalloff(); + + void SetWeight( float fWeight ); + +protected: + void StartFade( float flDuration ); + float GetFadeRatio() const; + bool IsFadeTimeElapsed() const; +#else + void ClientThink(); + +private: +#endif // MAPBASE + Vector m_vecOrigin; + + float m_minFalloff; + float m_maxFalloff; + float m_flCurWeight; + char m_netLookupFilename[MAX_PATH]; + + bool m_bEnabled; + +#ifdef MAPBASE // From Alien Swarm SDK + float m_flFadeInDuration; + float m_flFadeOutDuration; + float m_flMaxWeight; + + bool m_bMaster; + bool m_bClientSide; + bool m_bExclusive; + + bool m_bFadingIn; + float m_flFadeStartWeight; + float m_flFadeStartTime; + float m_flFadeDuration; +#endif // MAPBASE + + ClientCCHandle_t m_CCHandle; +}; + +#endif // C_COLORCORRECTION_H diff --git a/game/client/c_effects.cpp b/game/client/c_effects.cpp index 14a90a4cc..4fb738a37 100644 --- a/game/client/c_effects.cpp +++ b/game/client/c_effects.cpp @@ -6,6 +6,7 @@ // //=============================================================================// #include "cbase.h" +#include "c_effects.h" #include "c_tracer.h" #include "view.h" #include "initializer.h" @@ -22,6 +23,7 @@ #include "collisionutils.h" #include "tier0/vprof.h" #include "viewrender.h" +#include "raytrace.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -35,6 +37,15 @@ float g_flSplashLifetime = 0.5f; float g_flSplashAlpha = 0.3f; ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // N% chance of a rain particle making a splash. +ConVar r_RainParticleDensity( "r_RainParticleDensity", "1", FCVAR_NONE, "Density of Particle Rain 0-1" ); + +#ifdef MAPBASE +ConVar r_RainParticleClampOffset_Rain( "r_RainParticleClampOffset_Rain", "120", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain' type." ); +ConVar r_RainParticleClampOffset_Ash( "r_RainParticleClampOffset_Ash", "300", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Ash' type." ); +ConVar r_RainParticleClampOffset_RainStorm( "r_RainParticleClampOffset_RainStorm", "112", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain Storm' type." ); +ConVar r_RainParticleClampOffset_Snow( "r_RainParticleClampOffset_Snow", "300", FCVAR_NONE, "How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Snow' type." ); +ConVar r_RainParticleClampDebug( "r_RainParticleClampDebug", "0", FCVAR_NONE, "Enables debug code for precipitation particle system clamping" ); +#endif // MAPBASE float GUST_INTERVAL_MIN = 1; float GUST_INTERVAL_MAX = 2; @@ -60,151 +71,14 @@ CLIENTEFFECT_MATERIAL( "particle/rain" ) CLIENTEFFECT_MATERIAL( "particle/snow" ) CLIENTEFFECT_REGISTER_END() -//----------------------------------------------------------------------------- -// Precipitation particle type -//----------------------------------------------------------------------------- - -class CPrecipitationParticle -{ -public: - Vector m_Pos; - Vector m_Velocity; - float m_SpawnTime; // Note: Tweak with this to change lifetime - float m_Mass; - float m_Ramp; - - float m_flCurLifetime; - float m_flMaxLifetime; -}; - - -class CClient_Precipitation; -static CUtlVector g_Precipitations; - -//=========== -// Snow fall -//=========== -class CSnowFallManager; -static CSnowFallManager *s_pSnowFallMgr = NULL; -bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ); -void SnowFallManagerDestroy( void ); - -class AshDebrisEffect : public CSimpleEmitter -{ -public: - AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} - - static AshDebrisEffect* Create( const char *pDebugName ); - - virtual float UpdateAlpha( const SimpleParticle *pParticle ); - virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta ); - -private: - AshDebrisEffect( const AshDebrisEffect & ); -}; - -//----------------------------------------------------------------------------- -// Precipitation base entity -//----------------------------------------------------------------------------- - -class CClient_Precipitation : public C_BaseEntity -{ -class CPrecipitationEffect; -friend class CClient_Precipitation::CPrecipitationEffect; - -public: - DECLARE_CLASS( CClient_Precipitation, C_BaseEntity ); - DECLARE_CLIENTCLASS(); - - CClient_Precipitation(); - virtual ~CClient_Precipitation(); - - // Inherited from C_BaseEntity - virtual void Precache( ); - - void Render(); - -private: - - // Creates a single particle - CPrecipitationParticle* CreateParticle(); - - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual void ClientThink(); - - void Simulate( float dt ); - - // Renders the particle - void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ); - - void CreateWaterSplashes(); - - // Emits the actual particles - void EmitParticles( float fTimeDelta ); - - // Computes where we're gonna emit - bool ComputeEmissionArea( Vector& origin, Vector2D& size ); - - // Gets the tracer width and speed - float GetWidth() const; - float GetLength() const; - float GetSpeed() const; - - // Gets the remaining lifetime of the particle - float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const; - - // Computes the wind vector - static void ComputeWindVector( ); - - // simulation methods - bool SimulateRain( CPrecipitationParticle* pParticle, float dt ); - bool SimulateSnow( CPrecipitationParticle* pParticle, float dt ); - - void CreateAshParticle( void ); - void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); - - // Information helpful in creating and rendering particles - IMaterial *m_MatHandle; // material used - - float m_Color[4]; // precip color - float m_Lifetime; // Precip lifetime - float m_InitialRamp; // Initial ramp value - float m_Speed; // Precip speed - float m_Width; // Tracer width - float m_Remainder; // particles we should render next time - PrecipitationType_t m_nPrecipType; // Precip type - float m_flHalfScreenWidth; // Precalculated each frame. - - float m_flDensity; - - // Some state used in rendering and simulation - // Used to modify the rain density and wind from the console - static ConVar s_raindensity; - static ConVar s_rainwidth; - static ConVar s_rainlength; - static ConVar s_rainspeed; - - static Vector s_WindVector; // Stores the wind speed vector - - CUtlLinkedList m_Particles; - CUtlVector m_Splashes; - - CSmartPtr m_pAshEmitter; - TimedEvent m_tAshParticleTimer; - TimedEvent m_tAshParticleTraceTimer; - bool m_bActiveAshEmitter; - Vector m_vAshSpawnOrigin; - - int m_iAshCount; - -private: - CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible -}; - +CUtlVector< RayTracingEnvironment* > g_RayTraceEnvironments; // Just receive the normal data table stuff IMPLEMENT_CLIENTCLASS_DT(CClient_Precipitation, DT_Precipitation, CPrecipitation) - RecvPropInt( RECVINFO( m_nPrecipType ) ) + RecvPropInt( RECVINFO( m_nPrecipType ) ), +#ifdef MAPBASE + RecvPropInt( RECVINFO( m_spawnflags ) ), +#endif // MAPBASE END_RECV_TABLE() static ConVar r_SnowEnable( "r_SnowEnable", "1", FCVAR_CHEAT, "Snow Enable" ); @@ -396,6 +270,12 @@ inline bool CClient_Precipitation::SimulateSnow( CPrecipitationParticle* pPartic void CClient_Precipitation::Simulate( float dt ) { + if ( IsParticleRainType(m_nPrecipType) ) + { + CreateParticlePrecip(); + return; + } + // NOTE: When client-side prechaching works, we need to remove this Precache(); @@ -472,6 +352,9 @@ inline void CClient_Precipitation::RenderParticle( CPrecipitationParticle* pPart float scale; Vector start, delta; + if ( IsParticleRainType(m_nPrecipType) ) + return; + if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) return; @@ -562,6 +445,9 @@ void CClient_Precipitation::Render() if ( !r_DrawRain.GetInt() ) return; + if ( IsParticleRainType(m_nPrecipType) ) + return; + // Don't render in monitors or in reflections or refractions. if ( CurrentViewID() == VIEW_MONITOR ) return; @@ -632,6 +518,11 @@ CClient_Precipitation::CClient_Precipitation() : m_Remainder(0.0f) m_nPrecipType = PRECIPITATION_TYPE_RAIN; m_MatHandle = INVALID_MATERIAL_HANDLE; m_flHalfScreenWidth = 1; + + m_pParticlePrecipInnerNear = NULL; + m_pParticlePrecipInnerFar = NULL; + m_pParticlePrecipOuter = NULL; + m_bActiveParticlePrecipEmitter = false; g_Precipitations.AddToTail( this ); } @@ -1011,6 +902,395 @@ void CClient_Precipitation::CreateAshParticle( void ) } } +void CClient_Precipitation::PrecacheParticlePrecip( void ) +{ + if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH ) + { + PrecacheParticleSystem( "ash" ); + PrecacheParticleSystem( "ash_outer" ); + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW ) + { + PrecacheParticleSystem( "snow" ); + PrecacheParticleSystem( "snow_outer" ); + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM ) + { + PrecacheParticleSystem( "rain_storm" ); + PrecacheParticleSystem( "rain_storm_screen" ); + PrecacheParticleSystem( "rain_storm_outer" ); + } + else //default to rain + { + PrecacheParticleSystem( "rain" ); + PrecacheParticleSystem( "rain_outer" ); + } +} + +void CClient_Precipitation::CreateParticlePrecip( void ) +{ + if ( !m_bParticlePrecipInitialized ) + { + PrecacheParticlePrecip(); + InitializeParticlePrecip(); + } + + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( pPlayer == NULL ) + return; + + // Make sure the emitter is setup + if ( !m_bActiveParticlePrecipEmitter ) + { + //Update 8 times per second. + m_tParticlePrecipTraceTimer.Init( 8 ); + DestroyInnerParticlePrecip(); + DestroyOuterParticlePrecip(); + m_bActiveParticlePrecipEmitter = true; + } + + UpdateParticlePrecip( pPlayer ); +} + +#ifdef MAPBASE +void CClient_Precipitation::ClampParticlePosition( Vector &vPlayerPos, Vector &vOffsetPos, Vector &vOffsetPosNear, Vector &vOffsetPosFar ) +{ + Vector mins, maxs; + modelinfo->GetModelBounds( GetModel(), mins, maxs ); + + // Account for precipitation height + maxs.z += 180; + + Vector vecOrigin; //= WorldSpaceCenter(); + VectorLerp( mins, maxs, 0.5f, vecOrigin ); + + maxs -= vecOrigin; + mins -= vecOrigin; + + //float flMax = r_RainParticleClampOffset.GetFloat(); + float flMax = 0; + switch (m_nPrecipType) + { + case PRECIPITATION_TYPE_PARTICLERAIN: + flMax = r_RainParticleClampOffset_Rain.GetFloat(); + break; + + case PRECIPITATION_TYPE_PARTICLEASH: + flMax = r_RainParticleClampOffset_Ash.GetFloat(); + break; + + case PRECIPITATION_TYPE_PARTICLERAINSTORM: + flMax = r_RainParticleClampOffset_RainStorm.GetFloat(); + break; + + case PRECIPITATION_TYPE_PARTICLESNOW: + flMax = r_RainParticleClampOffset_Snow.GetFloat(); + break; + } + + Vector addend( flMax, flMax, 0 ); + mins += addend; + maxs -= addend; + + if (flMax > 0) + { + // Unless this is extruding outwards, make sure the offset isn't inverting the bounds. + // This means precipitation triggers with bounds less than offset*2 will turn into a thin line + // and the involved precipitation will pretty much be spatial at all times, which is okay. + mins.x = clamp( mins.x, -FLT_MAX, -1 ); + mins.y = clamp( mins.y, -FLT_MAX, -1 ); + maxs.x = clamp( maxs.x, 1, FLT_MAX ); + maxs.y = clamp( maxs.y, 1, FLT_MAX ); + } + + if (r_RainParticleClampDebug.GetBool()) + debugoverlay->AddBoxOverlay( vecOrigin, mins, maxs, vec3_angle, 255, 0, 0, 128, 0.15f ); + + maxs += vecOrigin; + mins += vecOrigin; + + CalcClosestPointOnAABB( mins, maxs, vPlayerPos, vPlayerPos ); + CalcClosestPointOnAABB( mins, maxs, vOffsetPos, vOffsetPos ); + CalcClosestPointOnAABB( mins, maxs, vOffsetPosNear, vOffsetPosNear ); + CalcClosestPointOnAABB( mins, maxs, vOffsetPosFar, vOffsetPosFar ); +} +#endif // MAPBASE + +void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer ) +{ + if ( !pPlayer ) + return; + + Vector vForward; + Vector vRight; + + pPlayer->GetVectors( &vForward, &vRight, NULL ); + vForward.z = 0.0f; + vForward.NormalizeInPlace(); + Vector vForward45Right = vForward + vRight; + Vector vForward45Left = vForward - vRight; + vForward45Right.NormalizeInPlace(); + vForward45Left.NormalizeInPlace(); + fltx4 TMax = ReplicateX4( 320.0f ); + SubFloat( TMax, 3 ) = FLT_MAX; + float curTime = gpGlobals->frametime; + + while ( m_tParticlePrecipTraceTimer.NextEvent( curTime ) ) + { + Vector vPlayerPos = pPlayer->EyePosition(); + Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 180 ); + Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 32 ); + Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 100 ); + +#ifdef MAPBASE + if (m_spawnflags & SF_PRECIP_PARTICLE_CLAMP) + { + ClampParticlePosition( vPlayerPos, vOffsetPos, vOffsetPosNear, vOffsetPosFar ); + } +#endif // MAPBASE + + Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity; + + // Get the rain volume Ray Tracing Environment. Currently hard coded to 0, should have this lookup + RayTracingEnvironment *RtEnv = g_RayTraceEnvironments.Element( 0 ); + + // Our 4 Rays are forward, off to the left and right, and directly up. + // Use the first three to determine if there's generally visible rain where we're looking. + // The forth, straight up, tells us if we're standing inside a rain volume + // (based on the normal that we hit or if we miss entirely) + FourRays frRays; + FourVectors fvDirection; + fvDirection = FourVectors( vForward, vForward45Left, vForward45Right, Vector( 0, 0, 1 ) ); + frRays.direction = fvDirection; + frRays.origin.DuplicateVector( vPlayerPos ); + RayTracingResult Result; + + RtEnv->Trace4Rays( frRays, Four_Zeros, TMax, &Result ); + + i32x4 in4HitIds = LoadAlignedIntSIMD( Result.HitIds ); + fltx4 fl4HitIds = SignedIntConvertToFltSIMD ( in4HitIds ); + + fltx4 fl4Tolerance = ReplicateX4( 300.0f ); + // ignore upwards test for tolerance, as we may be below an area which is raining, but with it not visible in front of us + //SubFloat( fl4Tolerance, 3 ) = 0.0f; + + bool bInside = ( Result.HitIds[3] != -1 && Result.surface_normal.Vec( 3 ).z < 0.0f ); + bool bNearby = ( IsAnyNegative ( CmpGeSIMD ( fl4HitIds, Four_Zeros ) ) && IsAnyNegative( CmpGeSIMD( fl4Tolerance, Result.HitDistance ) ) ); + + if ( bInside || bNearby ) + { + //We can see a rain volume, but it's farther than 180 units away, only use far effect. + if ( !bInside && SubFloat( FindLowestSIMD3( Result.HitDistance ), 0 ) >= m_flParticleInnerDist ) + { + // Kill the inner rain if it's previously been in use + if ( m_pParticlePrecipInnerNear != NULL ) + { + DestroyInnerParticlePrecip(); + } + // Update if we've already got systems, otherwise, create them. + if ( m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } + else + { + DispatchOuterParticlePrecip( pPlayer, vForward ); + } + } + else //We're close enough to use the near effect. + { + // Update if we've already got systems, otherwise, create them. +#ifdef MAPBASE + // The outer can now be suppressed without interfering with other functionality + if ( m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } + if ( m_pParticlePrecipInnerNear != NULL && m_pParticlePrecipInnerFar != NULL ) + { + m_pParticlePrecipInnerNear->SetControlPoint( 1, vOffsetPosNear ); + m_pParticlePrecipInnerFar->SetControlPoint( 1, vOffsetPosFar ); + m_pParticlePrecipInnerNear->SetControlPoint( 3, vDensity ); + m_pParticlePrecipInnerFar->SetControlPoint( 3, vDensity ); + } +#else + if ( m_pParticlePrecipInnerNear != NULL && m_pParticlePrecipInnerFar != NULL && m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipInnerNear->SetControlPoint( 1, vOffsetPosNear ); + m_pParticlePrecipInnerFar->SetControlPoint( 1, vOffsetPosFar ); + m_pParticlePrecipInnerNear->SetControlPoint( 3, vDensity ); + m_pParticlePrecipInnerFar->SetControlPoint( 3, vDensity ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } +#endif // MAPBASE + else + { + DispatchInnerParticlePrecip( pPlayer, vForward ); + } + } + } + else // No rain in the area, kill any leftover systems. + { + DestroyInnerParticlePrecip(); + DestroyOuterParticlePrecip(); + } + } +} + +void CClient_Precipitation::InitializeParticlePrecip( void ) +{ + //Set up which type of precipitation particle we'll use + if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH ) + { + m_pParticleInnerNearDef = "ash"; + m_pParticleInnerFarDef = "ash"; + m_pParticleOuterDef = "ash_outer"; + m_flParticleInnerDist = 280.0; + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW ) + { + m_pParticleInnerNearDef = "snow"; + m_pParticleInnerFarDef = "snow"; + m_pParticleOuterDef = "snow_outer"; + m_flParticleInnerDist = 280.0; + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM ) + { + m_pParticleInnerNearDef = "rain_storm"; + m_pParticleInnerFarDef = "rain_storm_screen"; + m_pParticleOuterDef = "rain_storm_outer"; + m_flParticleInnerDist = 0.0; + } + else //default to rain + { + m_pParticleInnerNearDef = "rain"; + m_pParticleInnerFarDef = "rain"; + m_pParticleOuterDef = "rain_outer"; + m_flParticleInnerDist = 180.0; + } + + Assert( m_pParticleInnerFarDef != NULL ); + + //We'll want to change this if/when we add more raytrace environments. + g_RayTraceEnvironments.PurgeAndDeleteElements(); + + // Sets up ray tracing environments for all func_precipitations and func_precipitation_blockers + RayTracingEnvironment *rtEnvRainEmission = new RayTracingEnvironment(); + g_RayTraceEnvironments.AddToTail( rtEnvRainEmission ); + RayTracingEnvironment *rtEnvRainBlocker = new RayTracingEnvironment(); + g_RayTraceEnvironments.AddToTail( rtEnvRainBlocker ); + + rtEnvRainEmission->Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram + rtEnvRainBlocker->Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram + + int nTriCount = 1; + for ( int i=0; iGetVCollide( volume->GetModelIndex() ); + + if ( !pCollide || pCollide->solidCount <= 0 ) + continue; + + Vector *outVerts; + int vertCount = physcollision->CreateDebugMesh( pCollide->solids[0], &outVerts ); + + if ( vertCount ) + { + for ( int j = 0; j < vertCount; j += 3 ) + { + rtEnvRainEmission->AddTriangle( nTriCount++, outVerts[j], outVerts[j + 1], outVerts[j + 2], Vector( 1, 1, 1 ) ); + } + } + physcollision->DestroyDebugMesh( vertCount, outVerts ); + } + rtEnvRainEmission->SetupAccelerationStructure(); + + m_bParticlePrecipInitialized = true; +} + +void CClient_Precipitation::DestroyInnerParticlePrecip( void ) +{ + if ( m_pParticlePrecipInnerFar != NULL ) + { + m_pParticlePrecipInnerFar->StopEmission(); + m_pParticlePrecipInnerFar = NULL; + } + if ( m_pParticlePrecipInnerNear != NULL ) + { + m_pParticlePrecipInnerNear->StopEmission(); + m_pParticlePrecipInnerNear = NULL; + } +} + +void CClient_Precipitation::DestroyOuterParticlePrecip( void ) +{ + if ( m_pParticlePrecipOuter != NULL ) + { + m_pParticlePrecipOuter->StopEmission(); + m_pParticlePrecipOuter = NULL; + } +} + +void CClient_Precipitation::DispatchOuterParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ) +{ + DestroyOuterParticlePrecip(); + +#ifdef MAPBASE + if (m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER) + return; +#endif // MAPBASE + + Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity; + Vector vPlayerPos = pPlayer->EyePosition(); + + m_pParticlePrecipOuter = ParticleProp()->Create( m_pParticleOuterDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipOuter->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipOuter->SetControlPoint( 1, vPlayerPos + Vector (0, 0, 180 ) ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); +} + +void CClient_Precipitation::DispatchInnerParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ) +{ + DestroyInnerParticlePrecip(); + DestroyOuterParticlePrecip(); + Vector vPlayerPos = pPlayer->EyePosition(); + Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 180 ); + Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 32 ); + Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * m_flParticleInnerDist ); // 100.0 + Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity; + +#ifdef MAPBASE + if (m_spawnflags & SF_PRECIP_PARTICLE_CLAMP) + { + ClampParticlePosition( vPlayerPos, vOffsetPos, vOffsetPosNear, vOffsetPosFar ); + } + + if (!(m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER)) +#endif // MAPBASE + { + m_pParticlePrecipOuter = ParticleProp()->Create( m_pParticleOuterDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipOuter->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipOuter->SetControlPoint( 1, vOffsetPos ); + m_pParticlePrecipOuter->SetControlPoint( 3, vDensity ); + } + + m_pParticlePrecipInnerNear = ParticleProp()->Create( m_pParticleInnerNearDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipInnerFar = ParticleProp()->Create( m_pParticleInnerFarDef, PATTACH_ABSORIGIN_FOLLOW ); + m_pParticlePrecipInnerNear->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipInnerFar->SetControlPointEntity( 2, pPlayer ); + m_pParticlePrecipInnerNear->SetControlPoint( 1, vOffsetPosNear ); + m_pParticlePrecipInnerFar->SetControlPoint( 1, vOffsetPosFar ); + m_pParticlePrecipInnerNear->SetControlPoint( 3, vDensity ); + m_pParticlePrecipInnerFar->SetControlPoint( 3, vDensity ); +} + void CClient_Precipitation::CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ) { // Create the particle @@ -1206,6 +1486,12 @@ BEGIN_RECV_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared) RecvPropFloat (RECVINFO(m_flStartTime)), RecvPropFloat (RECVINFO(m_flGustDuration)), // RecvPropInt (RECVINFO(m_iszGustSound)), +#ifdef MAPBASE + RecvPropFloat (RECVINFO(m_windRadius)), + RecvPropFloat (RECVINFO(m_windRadiusInner)), + RecvPropVector (RECVINFO(m_location)), + RecvPropFloat (RECVINFO(m_flTreeSwayScale)), +#endif // MAPBASE END_RECV_TABLE() IMPLEMENT_CLIENTCLASS_DT( C_EnvWind, DT_EnvWind, CEnvWind ) @@ -1540,8 +1826,12 @@ class SnowFallEffect : public CSimpleEmitter pParticle->m_vecVelocity *= flSpeed; +#ifdef MAPBASE + Vector vecWindVelocity = GetWindspeedAtLocation( pParticle->m_Pos ); +#else Vector vecWindVelocity; GetWindspeedAtTime( gpGlobals->curtime, vecWindVelocity ); +#endif // MAPBASE pParticle->m_vecVelocity += ( vecWindVelocity * r_SnowWindScale.GetFloat() ); } diff --git a/game/client/c_effects.h b/game/client/c_effects.h index 27bb501e9..478fcfcf4 100644 --- a/game/client/c_effects.h +++ b/game/client/c_effects.h @@ -10,9 +10,182 @@ #pragma once #endif +#include "cbase.h" +#include "precipitation_shared.h" // Draw rain effects. void DrawPrecipitation(); +//----------------------------------------------------------------------------- +// Precipitation particle type +//----------------------------------------------------------------------------- + +class CPrecipitationParticle +{ +public: + Vector m_Pos; + Vector m_Velocity; + float m_SpawnTime; // Note: Tweak with this to change lifetime + float m_Mass; + float m_Ramp; + + float m_flCurLifetime; + float m_flMaxLifetime; +}; + + +class CClient_Precipitation; +static CUtlVector g_Precipitations; + +//=========== +// Snow fall +//=========== +class CSnowFallManager; +static CSnowFallManager *s_pSnowFallMgr = NULL; +bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ); +void SnowFallManagerDestroy( void ); + +class AshDebrisEffect : public CSimpleEmitter +{ +public: + AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} + + static AshDebrisEffect* Create( const char *pDebugName ); + + virtual float UpdateAlpha( const SimpleParticle *pParticle ); + virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta ); + +private: + AshDebrisEffect( const AshDebrisEffect & ); +}; + +//----------------------------------------------------------------------------- +// Precipitation base entity +//----------------------------------------------------------------------------- + +class CClient_Precipitation : public C_BaseEntity +{ +class CPrecipitationEffect; +friend class CClient_Precipitation::CPrecipitationEffect; + +public: + DECLARE_CLASS( CClient_Precipitation, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + CClient_Precipitation(); + virtual ~CClient_Precipitation(); + + // Inherited from C_BaseEntity + virtual void Precache( ); + + void Render(); + +private: + + // Creates a single particle + CPrecipitationParticle* CreateParticle(); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void ClientThink(); + + void Simulate( float dt ); + + // Renders the particle + void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ); + + void CreateWaterSplashes(); + + // Emits the actual particles + void EmitParticles( float fTimeDelta ); + + // Computes where we're gonna emit + bool ComputeEmissionArea( Vector& origin, Vector2D& size ); + + // Gets the tracer width and speed + float GetWidth() const; + float GetLength() const; + float GetSpeed() const; + + // Gets the remaining lifetime of the particle + float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const; + + // Computes the wind vector + static void ComputeWindVector( ); + + // simulation methods + bool SimulateRain( CPrecipitationParticle* pParticle, float dt ); + bool SimulateSnow( CPrecipitationParticle* pParticle, float dt ); + + void PrecacheParticlePrecip( void ); + void CreateParticlePrecip( void ); + void InitializeParticlePrecip( void ); + void DispatchOuterParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ); + void DispatchInnerParticlePrecip( C_BasePlayer *pPlayer, Vector vForward ); + void DestroyOuterParticlePrecip( void ); + void DestroyInnerParticlePrecip( void ); + + void UpdateParticlePrecip( C_BasePlayer *pPlayer ); + +private: + void CreateAshParticle( void ); + void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); + +#ifdef MAPBASE + void ClampParticlePosition( Vector &vPlayerPos, Vector &vOffsetPos, Vector &vOffsetPosNear, Vector &vOffsetPosFar ); +#endif // MAPBASE + + // Information helpful in creating and rendering particles + IMaterial *m_MatHandle; // material used + + float m_Color[4]; // precip color + float m_Lifetime; // Precip lifetime + float m_InitialRamp; // Initial ramp value + float m_Speed; // Precip speed + float m_Width; // Tracer width + float m_Remainder; // particles we should render next time + PrecipitationType_t m_nPrecipType; // Precip type + float m_flHalfScreenWidth; // Precalculated each frame. + + float m_flDensity; + +#ifdef MAPBASE + int m_spawnflags; +#endif // MAPBASE + + // Some state used in rendering and simulation + // Used to modify the rain density and wind from the console + static ConVar s_raindensity; + static ConVar s_rainwidth; + static ConVar s_rainlength; + static ConVar s_rainspeed; + + static Vector s_WindVector; // Stores the wind speed vector + + CUtlLinkedList m_Particles; + CUtlVector m_Splashes; + + CSmartPtr m_pAshEmitter; + TimedEvent m_tAshParticleTimer; + TimedEvent m_tAshParticleTraceTimer; + bool m_bActiveAshEmitter; + Vector m_vAshSpawnOrigin; + + int m_iAshCount; + +protected: + float m_flParticleInnerDist; //The distance at which to start drawing the inner system + char *m_pParticleInnerNearDef; //Name of the first inner system + char *m_pParticleInnerFarDef; //Name of the second inner system + char *m_pParticleOuterDef; //Name of the outer system + HPARTICLEFFECT m_pParticlePrecipInnerNear; + HPARTICLEFFECT m_pParticlePrecipInnerFar; + HPARTICLEFFECT m_pParticlePrecipOuter; + TimedEvent m_tParticlePrecipTraceTimer; + bool m_bActiveParticlePrecipEmitter; + bool m_bParticlePrecipInitialized; + +private: + CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible +}; #endif // C_EFFECTS_H diff --git a/game/client/c_env_dof_controller.cpp b/game/client/c_env_dof_controller.cpp new file mode 100644 index 000000000..cc8421e21 --- /dev/null +++ b/game/client/c_env_dof_controller.cpp @@ -0,0 +1,88 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: Depth of field controller entity +// +//============================================================================= + +#include "cbase.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +extern bool g_bDOFEnabled; +extern float g_flDOFNearBlurDepth; +extern float g_flDOFNearFocusDepth; +extern float g_flDOFFarFocusDepth; +extern float g_flDOFFarBlurDepth; +extern float g_flDOFNearBlurRadius; +extern float g_flDOFFarBlurRadius; + +EHANDLE g_hDOFControllerInUse = NULL; + +class C_EnvDOFController : public C_BaseEntity +{ + DECLARE_CLASS( C_EnvDOFController, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + C_EnvDOFController(); + ~C_EnvDOFController(); + virtual void OnDataChanged( DataUpdateType_t updateType ); + +private: + bool m_bDOFEnabled; + float m_flNearBlurDepth; + float m_flNearFocusDepth; + float m_flFarFocusDepth; + float m_flFarBlurDepth; + float m_flNearBlurRadius; + float m_flFarBlurRadius; + +private: + C_EnvDOFController( const C_EnvDOFController & ); +}; + +IMPLEMENT_CLIENTCLASS_DT( C_EnvDOFController, DT_EnvDOFController, CEnvDOFController ) + RecvPropInt( RECVINFO(m_bDOFEnabled) ), + RecvPropFloat( RECVINFO(m_flNearBlurDepth) ), + RecvPropFloat( RECVINFO(m_flNearFocusDepth) ), + RecvPropFloat( RECVINFO(m_flFarFocusDepth) ), + RecvPropFloat( RECVINFO(m_flFarBlurDepth) ), + RecvPropFloat( RECVINFO(m_flNearBlurRadius) ), + RecvPropFloat( RECVINFO(m_flFarBlurRadius) ) +END_RECV_TABLE() + +C_EnvDOFController::C_EnvDOFController() +: m_bDOFEnabled( true ), + m_flNearBlurDepth( 20.0f ), + m_flNearFocusDepth( 100.0f ), + m_flFarFocusDepth( 250.0f ), + m_flFarBlurDepth( 1000.0f ), + m_flNearBlurRadius( 0.0f ), // no near blur by default + m_flFarBlurRadius( 5.0f ) +{ +} + +C_EnvDOFController::~C_EnvDOFController() +{ + if ( g_hDOFControllerInUse == this ) + { + g_bDOFEnabled = false; + } +} + +void C_EnvDOFController::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + g_bDOFEnabled = m_bDOFEnabled && ( ( m_flNearBlurRadius > 0.0f ) || ( m_flFarBlurRadius > 0.0f ) ); + g_flDOFNearBlurDepth = m_flNearBlurDepth; + g_flDOFNearFocusDepth = m_flNearFocusDepth; + g_flDOFFarFocusDepth = m_flFarFocusDepth; + g_flDOFFarBlurDepth = m_flFarBlurDepth; + g_flDOFNearBlurRadius = m_flNearBlurRadius; + g_flDOFFarBlurRadius = m_flFarBlurRadius; + + g_hDOFControllerInUse = this; +} diff --git a/game/client/c_env_global_light.cpp b/game/client/c_env_global_light.cpp new file mode 100644 index 000000000..1e3408ca9 --- /dev/null +++ b/game/client/c_env_global_light.cpp @@ -0,0 +1,348 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Sunlight shadow control entity. +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" + +#include "c_baseplayer.h" +#include "tier0/vprof.h" +#ifdef MAPBASE +#include "materialsystem/itexture.h" +#endif // MAPBASE + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +extern ConVar cl_sunlight_ortho_size; +extern ConVar cl_sunlight_depthbias; + +ConVar cl_globallight_freeze( "cl_globallight_freeze", "0" ); +#ifdef MAPBASE +// I imagine these values might've been designed for the ASW view. +// You can set these as KV anyway. +ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "0" ); +ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "0" ); + +static ConVar cl_globallight_slopescaledepthbias_shadowmap( "cl_globallight_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT ); +static ConVar cl_globallight_shadowfiltersize( "cl_globallight_shadowfiltersize", "0.1", FCVAR_CHEAT ); +static ConVar cl_globallight_depthbias_shadowmap( "cl_globallight_depthbias_shadowmap", "0.00001", FCVAR_CHEAT ); +static ConVar cl_globallight_depthres( "cl_globallight_depthres", "8192", FCVAR_CHEAT ); +#else +ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "-800" ); +ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "1600" ); +#endif // MAPBASE + +//------------------------------------------------------------------------------ +// Purpose : Sunlights shadow control entity +//------------------------------------------------------------------------------ +class C_GlobalLight : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_GlobalLight, C_BaseEntity ); + + DECLARE_CLIENTCLASS(); + + virtual ~C_GlobalLight(); + + void OnDataChanged( DataUpdateType_t updateType ); + void Spawn(); + bool ShouldDraw(); + + void ClientThink(); + +private: + Vector m_shadowDirection; + bool m_bEnabled; + char m_TextureName[ MAX_PATH ]; +#ifdef MAPBASE + int m_nSpotlightTextureFrame; +#endif + CTextureReference m_SpotlightTexture; + color32 m_LightColor; +#ifdef MAPBASE + float m_flBrightnessScale; + float m_flCurrentBrightnessScale; +#endif // MAPBASE + Vector m_CurrentLinearFloatLightColor; + float m_flCurrentLinearFloatLightAlpha; + float m_flColorTransitionTime; + float m_flSunDistance; + float m_flFOV; + float m_flNearZ; + float m_flNorthOffset; +#ifdef MAPBASE + float m_flEastOffset; + float m_flForwardOffset; + float m_flOrthoSize; +#endif // MAPBASE + bool m_bEnableShadows; + bool m_bOldEnableShadows; + + static ClientShadowHandle_t m_LocalFlashlightHandle; +}; + + +ClientShadowHandle_t C_GlobalLight::m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + + +IMPLEMENT_CLIENTCLASS_DT(C_GlobalLight, DT_GlobalLight, CGlobalLight) + RecvPropVector(RECVINFO(m_shadowDirection)), + RecvPropBool(RECVINFO(m_bEnabled)), + RecvPropString(RECVINFO(m_TextureName)), +#ifdef MAPBASE + RecvPropInt(RECVINFO(m_nSpotlightTextureFrame)), +#endif // MAPBASE + /*RecvPropInt(RECVINFO(m_LightColor), 0, RecvProxy_Int32ToColor32),*/ + RecvPropInt(RECVINFO(m_LightColor), 0, RecvProxy_IntToColor32), +#ifdef MAPBASE + RecvPropFloat(RECVINFO(m_flBrightnessScale)), +#endif // MAPBASE + RecvPropFloat(RECVINFO(m_flColorTransitionTime)), + RecvPropFloat(RECVINFO(m_flSunDistance)), + RecvPropFloat(RECVINFO(m_flFOV)), + RecvPropFloat(RECVINFO(m_flNearZ)), + RecvPropFloat(RECVINFO(m_flNorthOffset)), +#ifdef MAPBASE + RecvPropFloat(RECVINFO(m_flEastOffset)), + RecvPropFloat(RECVINFO(m_flForwardOffset)), + RecvPropFloat(RECVINFO(m_flOrthoSize)), +#endif // MAPBASE + RecvPropBool(RECVINFO(m_bEnableShadows)), +END_RECV_TABLE() + + +C_GlobalLight::~C_GlobalLight() +{ + if ( m_LocalFlashlightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LocalFlashlightHandle ); + m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + } +} + +void C_GlobalLight::OnDataChanged( DataUpdateType_t updateType ) +{ + if ( updateType == DATA_UPDATE_CREATED ) + { + m_SpotlightTexture.Init( m_TextureName, TEXTURE_GROUP_OTHER, true ); + } +#ifdef MAPBASE + else //if ( updateType == DATA_UPDATE_DATATABLE_CHANGED ) + { + // It could've been changed via input + if( !FStrEq(m_SpotlightTexture->GetName(), m_TextureName) ) + { + m_SpotlightTexture.Init( m_TextureName, TEXTURE_GROUP_OTHER, true ); + } + } +#endif // MAPBASE + + BaseClass::OnDataChanged( updateType ); +} + +void C_GlobalLight::Spawn() +{ + BaseClass::Spawn(); + + m_bOldEnableShadows = m_bEnableShadows; + + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +//------------------------------------------------------------------------------ +// We don't draw... +//------------------------------------------------------------------------------ +bool C_GlobalLight::ShouldDraw() +{ + return false; +} + +void C_GlobalLight::ClientThink() +{ + VPROF("C_GlobalLight::ClientThink"); + + bool bSupressWorldLights = false; + + if ( cl_globallight_freeze.GetBool() == true ) + { + return; + } + + if ( m_bEnabled ) + { + Vector vLinearFloatLightColor( m_LightColor.r, m_LightColor.g, m_LightColor.b ); + float flLinearFloatLightAlpha = m_LightColor.a; + +#ifdef MAPBASE + if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha || m_flCurrentBrightnessScale != m_flBrightnessScale ) + { + if (m_flColorTransitionTime != 0.0f) + { + float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f; + + m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed ); + m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed ); + m_flCurrentBrightnessScale = Approach( m_flBrightnessScale, m_flCurrentBrightnessScale, flColorTransitionSpeed ); + } + else + { + // Just do it instantly + m_CurrentLinearFloatLightColor.x = vLinearFloatLightColor.x; + m_CurrentLinearFloatLightColor.y = vLinearFloatLightColor.y; + m_CurrentLinearFloatLightColor.z = vLinearFloatLightColor.z; + m_flCurrentLinearFloatLightAlpha = flLinearFloatLightAlpha; + m_flCurrentBrightnessScale = m_flBrightnessScale; + } + } +#else + if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha ) + { + float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f; + + m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed ); + m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed ); + m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed ); + } +#endif // MAPBASE + + FlashlightState_t state; + + Vector vDirection = m_shadowDirection; + VectorNormalize( vDirection ); + + //Vector vViewUp = Vector( 0.0f, 1.0f, 0.0f ); + Vector vSunDirection2D = vDirection; + vSunDirection2D.z = 0.0f; + + /*HACK_GETLOCALPLAYER_GUARD( "C_GlobalLight::ClientThink" );*/ + + if ( !C_BasePlayer::GetLocalPlayer() ) + return; + + Vector vPos; + QAngle EyeAngles; + float flZNear, flZFar, flFov; + + C_BasePlayer::GetLocalPlayer()->CalcView( vPos, EyeAngles, flZNear, flZFar, flFov ); +// Vector vPos = C_BasePlayer::GetLocalPlayer()->GetAbsOrigin(); + +// vPos = Vector( 0.0f, 0.0f, 500.0f ); + vPos = ( vPos + vSunDirection2D * m_flNorthOffset ) - vDirection * m_flSunDistance; +#ifdef MAPBASE + vPos += Vector( m_flEastOffset + cl_globallight_xoffset.GetFloat(), m_flForwardOffset + cl_globallight_yoffset.GetFloat(), 0.0f ); +#else + vPos += Vector( cl_globallight_xoffset.GetFloat(), cl_globallight_yoffset.GetFloat(), 0.0f ); +#endif // MAPBASE + + QAngle angAngles; + VectorAngles( vDirection, angAngles ); + + Vector vForward, vRight, vUp; + AngleVectors( angAngles, &vForward, &vRight, &vUp ); + + state.m_fHorizontalFOVDegrees = m_flFOV; + state.m_fVerticalFOVDegrees = m_flFOV; + + state.m_vecLightOrigin = vPos; + BasisToQuaternion( vForward, vRight, vUp, state.m_quatOrientation ); + + state.m_fQuadraticAtten = 0.0f; + state.m_fLinearAtten = m_flSunDistance * 2.0f; + state.m_fConstantAtten = 0.0f; + state.m_FarZAtten = m_flSunDistance * 2.0f; +#ifdef MAPBASE + float flAlpha = m_flCurrentLinearFloatLightAlpha * (1.0f / 255.0f); + state.m_Color[0] = (m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[1] = (m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; + state.m_Color[2] = (m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * flAlpha) * m_flCurrentBrightnessScale; +#else + state.m_Color[0] = m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * m_flCurrentLinearFloatLightAlpha; + state.m_Color[1] = m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * m_flCurrentLinearFloatLightAlpha; + state.m_Color[2] = m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * m_flCurrentLinearFloatLightAlpha; +#endif // MAPBASE + state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient; + state.m_NearZ = 4.0f; + state.m_FarZ = m_flSunDistance * 2.0f; + state.m_fBrightnessScale = 2.0f; + state.m_bGlobalLight = true; + +#ifdef MAPBASE + float flOrthoSize = m_flOrthoSize; +#else + float flOrthoSize = 1000.0f; +#endif // MAPBASE + + if ( flOrthoSize > 0 ) + { + state.m_bOrtho = true; + state.m_fOrthoLeft = -flOrthoSize; + state.m_fOrthoTop = -flOrthoSize; + state.m_fOrthoRight = flOrthoSize; + state.m_fOrthoBottom = flOrthoSize; + } + else + { + state.m_bOrtho = false; + } + +#ifdef MAPBASE + //state.m_bDrawShadowFrustum = true; // Don't draw that huge debug thing + state.m_flShadowMapResolution = cl_globallight_depthres.GetFloat(); + state.m_flShadowFilterSize = cl_globallight_shadowfiltersize.GetFloat(); + state.m_flShadowSlopeScaleDepthBias = cl_globallight_slopescaledepthbias_shadowmap.GetFloat(); + state.m_flShadowDepthBias = cl_globallight_depthbias_shadowmap.GetFloat(); + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = m_SpotlightTexture; + state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; +#else + state.m_bDrawShadowFrustum = true; + /*state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();; + state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();*/ + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = m_SpotlightTexture; + state.m_nSpotlightTextureFrame = 0; +#endif // MAPBASE + + state.m_nShadowQuality = 1; // Allow entity to affect shadow quality +// state.m_bShadowHighRes = true; + + if ( m_bOldEnableShadows != m_bEnableShadows ) + { + // If they change the shadow enable/disable, we need to make a new handle + if ( m_LocalFlashlightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LocalFlashlightHandle ); + m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + } + + m_bOldEnableShadows = m_bEnableShadows; + } + + if( m_LocalFlashlightHandle == CLIENTSHADOW_INVALID_HANDLE ) + { + m_LocalFlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state ); + } + else + { + g_pClientShadowMgr->UpdateFlashlightState( m_LocalFlashlightHandle, state ); + g_pClientShadowMgr->UpdateProjectedTexture( m_LocalFlashlightHandle, true ); + } + + bSupressWorldLights = m_bEnableShadows; + } + else if ( m_LocalFlashlightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LocalFlashlightHandle ); + m_LocalFlashlightHandle = CLIENTSHADOW_INVALID_HANDLE; + } + + g_pClientShadowMgr->SetShadowFromWorldLightsEnabled( !bSupressWorldLights ); + + BaseClass::ClientThink(); +} diff --git a/game/client/cdll_util.cpp b/game/client/cdll_util.cpp index 4e877530b..1c5a6caa2 100644 --- a/game/client/cdll_util.cpp +++ b/game/client/cdll_util.cpp @@ -729,11 +729,14 @@ CBaseEntity *CEntitySphereQuery::GetCurrentEntity() // sep - Character to use as separator. UNDONE: allow multiple separator chars // Output : Returns a pointer to the next token to be parsed. //----------------------------------------------------------------------------- -const char *nexttoken(char *token, const char *str, char sep) +const char *nexttoken(char *token, const char *str, char sep, size_t tokenLen) { if ((str == NULL) || (*str == '\0')) { - *token = '\0'; + if(tokenLen) + { + *token = '\0'; + } return(NULL); } diff --git a/game/client/cdll_util.h b/game/client/cdll_util.h index 64beb5fbc..6e17327f1 100644 --- a/game/client/cdll_util.h +++ b/game/client/cdll_util.h @@ -89,7 +89,7 @@ void NormalizeAngles( QAngle& angles ); void InterpolateAngles( const QAngle& start, const QAngle& end, QAngle& output, float frac ); void InterpolateVector( float frac, const Vector& src, const Vector& dest, Vector& output ); -const char *nexttoken(char *token, const char *str, char sep); +const char *nexttoken(char *token, const char *str, char sep, size_t tokenLen); //----------------------------------------------------------------------------- // Base light indices to avoid index collision diff --git a/game/client/client_base.vpc b/game/client/client_base.vpc index 90e16cb81..09c55158a 100644 --- a/game/client/client_base.vpc +++ b/game/client/client_base.vpc @@ -177,6 +177,7 @@ $Project $File "$SRCDIR\game\shared\achievementmgr.h" $File "$SRCDIR\game\shared\achievements_and_stats_interface.h" $File "$SRCDIR\game\shared\achievements_hlx.cpp" + $File "$SRCDIR\game\shared\ai_criteria_new.cpp" $File "achievement_notification_panel.cpp" $File "achievement_notification_panel.h" $File "$SRCDIR\game\shared\activitylist.cpp" @@ -214,6 +215,7 @@ $Project $File "c_basedoor.cpp" $File "c_baseentity.cpp" $File "c_baseflex.cpp" + $File "c_baselesson.cpp" $File "c_baseplayer.cpp" $File "c_baseviewmodel.cpp" $File "c_breakableprop.cpp" @@ -683,6 +685,7 @@ $Project $File "c_basedoor.h" $File "c_baseentity.h" $File "c_baseflex.h" + $File "c_baselesson.h" $File "c_baseplayer.h" $File "c_basetempentity.h" $File "c_baseviewmodel.h" @@ -1098,10 +1101,11 @@ $Project $File "$SRCDIR\public\haptics\haptic_utils.h" [$WIN32] } - $Folder "Game Shared Header Files" + $Folder "Shared Header Files" { $File "$SRCDIR\game\shared\activitylist.h" $File "$SRCDIR\game\shared\ai_activity.h" + $File "$SRCDIR\game\shared\ai_criteria_new.h" $File "$SRCDIR\game\shared\ai_debug_shared.h" $File "$SRCDIR\game\shared\ammodef.h" $File "$SRCDIR\game\shared\animation.h" diff --git a/game/client/wscript b/game/client/wscript index c454d535c..5894326e2 100755 --- a/game/client/wscript +++ b/game/client/wscript @@ -61,6 +61,7 @@ def build(bld): 'steam_api', 'bitmap', 'vtf', + 'vscript' 'RT', 'ZLIB' ] diff --git a/game/server/server_base.vpc b/game/server/server_base.vpc index 6d78d55dc..802183295 100644 --- a/game/server/server_base.vpc +++ b/game/server/server_base.vpc @@ -124,6 +124,9 @@ $Project $File "ai_condition.h" $File "AI_Criteria.cpp" $File "AI_Criteria.h" + $File "$SRCDIR\game\shared\ai_criteria_new.cpp" + $File "$SRCDIR\game\shared\ai_criteria_new.h" + $File "ai_criteria_new.h" $File "ai_debug.h" $File "$SRCDIR\game\shared\ai_debug_shared.h" $File "ai_default.cpp" diff --git a/game/shared/GameEventListener.h b/game/shared/GameEventListener.h index 42d2ebaa3..9378257d7 100644 --- a/game/shared/GameEventListener.h +++ b/game/shared/GameEventListener.h @@ -25,7 +25,7 @@ class CGameEventListener : public IGameEventListener2 { } - ~CGameEventListener() + virtual ~CGameEventListener() { StopListeningForAllEvents(); } diff --git a/game/shared/Multiplayer/multiplayer_animstate.h b/game/shared/Multiplayer/multiplayer_animstate.h index 007533a12..c88dbf2b7 100644 --- a/game/shared/Multiplayer/multiplayer_animstate.h +++ b/game/shared/Multiplayer/multiplayer_animstate.h @@ -289,7 +289,6 @@ class CMultiPlayerAnimState // Pose parameters. bool m_bPoseParameterInit; MultiPlayerPoseData_t m_PoseParameterData; - DebugPlayerAnimData_t m_DebugAnimData; bool m_bCurrentFeetYawInitialized; float m_flLastAnimationStateClearTime; @@ -304,7 +303,7 @@ class CMultiPlayerAnimState // Jumping. bool m_bJumping; - float m_flJumpStartTime; + float m_flJumpStartTime; bool m_bFirstJumpFrame; // Swimming. @@ -334,6 +333,13 @@ class CMultiPlayerAnimState // movement playback options int m_nMovementSequence; LegAnimType_t m_LegAnimType; + + // Tony; moved debuganim data to a private block and made the 2 sdk animstates friendly. + // I override the base classes but want complete functionality. +private: + friend class CSDKPlayerAnimState; + friend class CHL2MPPlayerAnimState; + DebugPlayerAnimData_t m_DebugAnimData; }; // If this is set, then the game code needs to make sure to send player animation events diff --git a/game/shared/SoundEmitterSystem.cpp b/game/shared/SoundEmitterSystem.cpp index 6d27fde9a..2d3bc2e72 100644 --- a/game/shared/SoundEmitterSystem.cpp +++ b/game/shared/SoundEmitterSystem.cpp @@ -130,6 +130,26 @@ void Hack_FixEscapeChars( char *str ) Q_strncpy( str, osave, len ); } +#ifdef MAPBASE +static const ConVar *pHostTimescale; +static ConVar host_pitchscale( "host_pitchscale", "-1", FCVAR_REPLICATED, "If greater than 0, controls the pitch scale of sounds instead of host_timescale." ); + +static float GetSoundPitchScale() +{ + static ConVarRef sv_cheats( "sv_cheats" ); + if (sv_cheats.GetBool()) + { + if (host_pitchscale.GetFloat() > 0.0f) + return host_pitchscale.GetFloat(); + else + return pHostTimescale->GetFloat(); + } + + return 1.0f; +} +#endif // MAPBASE + + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -323,6 +343,10 @@ class CSoundEmitterSystem : public CBaseGameSystem } } #endif + +#ifdef MAPBASE + pHostTimescale = cvar->FindVar( "host_timescale" ); +#endif // MAPBASE } virtual void LevelInitPostEntity() @@ -525,7 +549,11 @@ class CSoundEmitterSystem : public CBaseGameSystem params.volume, (soundlevel_t)params.soundlevel, ep.m_nFlags, +#ifdef MAPBASE + params.pitch * GetSoundPitchScale(), +#else params.pitch, +#endif // MAPBASE ep.m_nSpecialDSP, ep.m_pOrigin, NULL, @@ -597,19 +625,23 @@ class CSoundEmitterSystem : public CBaseGameSystem } #endif enginesound->EmitSound( - filter, - entindex, - ep.m_nChannel, - ep.m_pSoundName, - ep.m_flVolume, - ep.m_SoundLevel, - ep.m_nFlags, - ep.m_nPitch, + filter, + entindex, + ep.m_nChannel, + ep.m_pSoundName, + ep.m_flVolume, + ep.m_SoundLevel, + ep.m_nFlags, +#ifdef MAPBASE + ep.m_nPitch * GetSoundPitchScale(), +#else + ep.m_nPitch, +#endif // MAPBASE ep.m_nSpecialDSP, ep.m_pOrigin, - NULL, + NULL, &ep.m_UtlVecSoundOrigin, - true, + true, ep.m_flSoundTime, ep.m_nSpeakerEntity ); if ( ep.m_pflSoundDuration ) @@ -826,6 +858,10 @@ class CSoundEmitterSystem : public CBaseGameSystem params.volume = flVolume; } +#ifdef MAPBASE + params.pitch *= GetSoundPitchScale(); +#endif // MAPBASE + #if defined( CLIENT_DLL ) enginesound->EmitAmbientSound( params.soundname, params.volume, params.pitch, iFlags, soundtime ); #else @@ -954,6 +990,10 @@ class CSoundEmitterSystem : public CBaseGameSystem if ( pSample && ( Q_stristr( pSample, ".wav" ) || Q_stristr( pSample, ".mp3" )) ) { +#ifdef MAPBASE + pitch *= GetSoundPitchScale(); +#endif // MAPBASE + #if defined( CLIENT_DLL ) enginesound->EmitAmbientSound( pSample, volume, pitch, flags, soundtime ); #else @@ -1187,6 +1227,24 @@ void CBaseEntity::EmitSound( const char *soundname, HSOUNDSCRIPTHANDLE& handle, EmitSound( filter, entindex(), params, handle ); } +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) +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 ); + return duration; +} +#endif // !CLIENT || MAPBASE_VSCRIPT + //----------------------------------------------------------------------------- // Purpose: // Input : filter - @@ -1366,6 +1424,52 @@ int SENTENCEG_Lookup(const char *sample) } #endif +#if defined(MAPBASE) && defined(GAME_DLL) +//----------------------------------------------------------------------------- +// Purpose: Wrapper to emit a sentence and also a close caption token for the sentence as appropriate. +// Input : filter - +// iEntIndex - +// iChannel - +// iSentenceIndex - +// flVolume - +// iSoundlevel - +// iFlags - +// iPitch - +// bUpdatePositions - +// soundtime - +//----------------------------------------------------------------------------- +void CBaseEntity::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex, + float flVolume, soundlevel_t iSoundlevel, int iFlags /*= 0*/, int iPitch /*=PITCH_NORM*/, + const Vector *pOrigin /*=NULL*/, const Vector *pDirection /*=NULL*/, + bool bUpdatePositions /*=true*/, float soundtime /*=0.0f*/, int iSpecialDSP /*= 0*/, int iSpeakerIndex /*= 0*/ ) +{ + CUtlVector< Vector > soundOrigins; + + bool bSwallowed = CEnvMicrophone::OnSentencePlayed( + iEntIndex, + iSentenceIndex, + iSoundlevel, + flVolume, + iFlags, + iPitch, + pOrigin, + soundtime, + soundOrigins ); + if ( bSwallowed ) + return; + + CBaseEntity *pEntity = UTIL_EntityByIndex( iEntIndex ); + if ( pEntity ) + { + pEntity->ModifySentenceParams( iSentenceIndex, iChannel, flVolume, iSoundlevel, iFlags, iPitch, + &pOrigin, &pDirection, bUpdatePositions, soundtime, iSpecialDSP, iSpeakerIndex ); + } + + enginesound->EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex, + flVolume, iSoundlevel, iFlags, iPitch * GetSoundPitchScale(), iSpecialDSP, pOrigin, pDirection, &soundOrigins, bUpdatePositions, soundtime, iSpeakerIndex ); +} +#endif // MAPBASE && GAME_DLL + void UTIL_EmitAmbientSound( int entindex, const Vector &vecOrigin, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ ) { #ifdef STAGING_ONLY @@ -1420,7 +1524,17 @@ static const char *UTIL_TranslateSoundName( const char *soundname, const char *a void CBaseEntity::GenderExpandString( char const *in, char *out, int maxlen ) { +#ifdef MAPBASE + // This is so models without globalactors.txt declarations can still play scenes. + gender_t gender = soundemitterbase->GetActorGender(STRING(GetModelName())); + + if (gender == GENDER_NONE) // Agender models when? + gender = GENDER_MALE; + + soundemitterbase->GenderExpandString(gender, in, out, maxlen); +#else soundemitterbase->GenderExpandString( STRING( GetModelName() ), in, out, maxlen ); +#endif // MAPBASE } bool CBaseEntity::GetParametersForSound( const char *soundname, CSoundParameters ¶ms, const char *actormodel ) @@ -1446,6 +1560,14 @@ HSOUNDSCRIPTHANDLE CBaseEntity::PrecacheScriptSound( const char *soundname ) #endif } +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) +// Same as server version of above, but signiture changed so it can be deduced by the macros +void CBaseEntity::VScriptPrecacheScriptSound(const char* soundname) +{ + g_SoundEmitterSystem.PrecacheScriptSound(soundname); +} +#endif // !CLIENT_DLL || MAPBASE_VSCRIPT + void CBaseEntity::PrefetchScriptSound( const char *soundname ) { g_SoundEmitterSystem.PrefetchScriptSound( soundname ); diff --git a/game/shared/achievementmgr.cpp b/game/shared/achievementmgr.cpp index 33970f5d2..618a146a6 100644 --- a/game/shared/achievementmgr.cpp +++ b/game/shared/achievementmgr.cpp @@ -1542,7 +1542,11 @@ void CAchievementMgr::OnKillEvent( CBaseEntity *pVictim, CBaseEntity *pAttacker, } CBaseCombatCharacter *pBCC = dynamic_cast( pVictim ); +#ifdef MAPBASE + if ( pBCC && ( D_FR >= pBCC->IRelationType( pLocalPlayer ) ) ) +#else if ( pBCC && ( D_HT == pBCC->IRelationType( pLocalPlayer ) ) ) +#endif // MAPBASE { bVictimIsPlayerEnemy = true; } @@ -1652,6 +1656,13 @@ void CAchievementMgr::OnMapEvent( const char *pchEventName ) CBaseAchievement *pAchievement = m_vecMapEventListeners[iAchievement]; pAchievement->OnMapEvent( pchEventName ); } + +#ifdef MAPBASE + if (cc_achievement_debug.GetBool()) + { + Msg( "CAchievementMgr::OnMapEvent: Achievement \"%s\" not found\n", pchEventName ); + } +#endif } //----------------------------------------------------------------------------- diff --git a/game/shared/activitylist.cpp b/game/shared/activitylist.cpp index 2b4212525..a4eb72cf7 100644 --- a/game/shared/activitylist.cpp +++ b/game/shared/activitylist.cpp @@ -2275,6 +2275,522 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_SPELL_VM_ARM ); REGISTER_SHARED_ACTIVITY( ACT_SPELL_VM_FIRE ); +#if AR2_ACTIVITY_FIX == 1 + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); + + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR2_LOW ); +#endif // AR2_ACTIVITY_FIX + +#if SHARED_COMBINE_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_COMBINE_THROW_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ); + + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_THROW_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK2 ); + + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_ADVANCE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_FORWARD ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_GROUP ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_HALT ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_LEFT ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_RIGHT ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_TAKECOVER ); +#endif // SHARED_COMBINE_ACTIVITIES + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_IDLE_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_REVOLVER ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_CROSSBOW ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_AIM_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_AIM_PISTOL ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_SHOTGUN_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_LOW ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_RPG ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_ANNABELLE ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_ANNABELLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_ANNABELLE ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_ANNABELLE ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_ANNABELLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_ANNABELLE ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_MELEE ); + + REGISTER_SHARED_ACTIVITY( ACT_RUN_PACKAGE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SUITCASE ); + + REGISTER_SHARED_ACTIVITY( ACT_ARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_MELEE ); +#endif // EXPANDED_HL2_WEAPON_ACTIVITIES + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR1 ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR1_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR1 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR3 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AR3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_AR3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG2 ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_SMG2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG2_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_SMG2 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG2_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG2_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG2_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG3_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_SMG3 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG3_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SMG3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SMG3_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SMG3_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_HMG1 ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_HMG1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_HMG1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_HMG1_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_HMG1_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_HMG1 ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_HMG1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_HMG1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_HMG1_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_HMG1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_HMG1_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_HMG1_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SNIPER_RIFLE ); + //REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_SNIPER_RIFLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_SNIPER_RIFLE_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SNIPER_RIFLE_LOW ); + //REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_SNIPER_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SNIPER_RIFLE_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SNIPER_RIFLE_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SNIPER_RIFLE_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SNIPER_RIFLE_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SNIPER_RIFLE_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SNIPER_RIFLE_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_DUAL_PISTOLS_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_DUAL_PISTOLS ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_DUAL_PISTOLS_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_DUAL_PISTOLS_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_DUAL_PISTOLS_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_DUAL_PISTOLS_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_DUAL_PISTOLS_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_DUAL_PISTOLS_STIMULATED ); +#endif // EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + +#if EXPANDED_NAVIGATION_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_ALL ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_IDLE ); + + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_TOP ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_BOTTOM ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_DISMOUNT_BOTTOM ); +#endif // EXPANDED_NAVIGATION_ACTIVITIES + +#if EXPANDED_HL2_COVER_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_MED ); + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG3_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_HMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_HMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SNIPER_RIFLE_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SNIPER_RIFLE_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_DUAL_PISTOLS_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_DUAL_PISTOLS_MED ); +#endif // EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_PISTOL ); +#endif // EXPANDED_HL2_COVER_ACTIVITIES + +#if EXPANDED_HL2DM_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_PHYSGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SLAM ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_PHYSGUN ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_REVOLVER ); + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_AR1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_AR1 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_AR3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_AR3 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SMG2 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SMG2 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SMG3 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SMG3 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_HMG1 ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_HMG1 ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_SNIPER_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_DUAL_PISTOLS ); +#endif // EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_USE ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_USE ); + + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_RUN_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_IDLE_CROUCH_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_WALK_CROUCH_USE_HEAVY ); + REGISTER_SHARED_ACTIVITY( ACT_HL2MP_JUMP_USE_HEAVY ); +#endif // EXPANDED_HL2DM_ACTIVITIES + AssertMsg( g_HighestActivity == LAST_SHARED_ACTIVITY - 1, "Not all activities from ai_activity.h registered in activitylist.cpp" ); } diff --git a/game/shared/ai_activity.h b/game/shared/ai_activity.h index fbd10c390..6f889c3ac 100644 --- a/game/shared/ai_activity.h +++ b/game/shared/ai_activity.h @@ -11,6 +11,57 @@ #pragma once #endif +#ifdef MAPBASE + +// +// Mapbase adds many new shared activities, primarily for NPCs. +// +// These are at the bottom of the enum to prevent disruptions in the order of existing activities. +// + +// AR2 ACTIVITY FIX +// Citizens and Combine soldiers have several activities for the SMG1 and AR2 which differ from each other. +// Across both NPCs, there are around 20-40 different AR2 animations. Most of these animations are similar to the +// SMG1 animations, except their hand positions are adjusted to use the AR2 instead. +// +// Unfortunately, the vast majority of the AR2 animations in those models are not declared as real activities in +// code. The AR2 instead falls back to its SMG1 animation counterparts. +// This is thought to be an oversight which dates back to late in Half-Life 2's development. +// +// This preprocessor declares those activities and implements them on the AR2. In-game, they work surprisingly well +// despite presumably never being tested in-game during HL2's development. +#define AR2_ACTIVITY_FIX 1 + +// SHARED COMBINE ACTIVITIES +// This turns ACT_COMBINE_AR2_ALTFIRE, ACT_COMBINE_THROW_GRENADE, and their new gesture counterparts into shared activities. +// This is necessary for other NPCs to use them without having to rely on private custom activities declared through the AI definition system. +#define SHARED_COMBINE_ACTIVITIES 1 + +// EXPANDED HL2 WEAPON ACTIVITIES +// This enables a bunch of new activities for Half-Life 2 weapons, including new 357 animations and readiness activities for pistols. +#define EXPANDED_HL2_WEAPON_ACTIVITIES 1 + +// EXPANDED HL2 UNUSED WEAPON ACTIVITIES +// This enables a bunch of new activities for unused Half-Life 2 weapons, particularly those which exist in the SDK, but are deactivated by default. +// This essentially just means mods which restore those weapons have the option of using custom activities for them. +// Mapbase's backup activity system would allow them to fall back to other weapons if the relevant activities do not exist. +// Also includes activity names for "AR3", "SMG3", and "DUAL_PISTOLS", which were never used in HL2, but may be useful when additional animation sets are needed. +#define EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES 0 + +// EXPANDED NAVIGATION ACTIVITIES +// This enables some new navigation-related activities. +#define EXPANDED_NAVIGATION_ACTIVITIES 1 + +// EXPANDED HL2 COVER ACTIVITIES +// This enables some new cover-related activities. +#define EXPANDED_HL2_COVER_ACTIVITIES 1 + +// EXPANDED HL2DM ACTIVITIES +// This enables some new activities for the HL2:DM set. +#define EXPANDED_HL2DM_ACTIVITIES 1 + +#endif // MAPBASE + #define ACTIVITY_NOT_AVAILABLE -1 typedef enum @@ -2107,10 +2158,546 @@ typedef enum ACT_SPELL_VM_ARM, ACT_SPELL_VM_FIRE, +#if AR2_ACTIVITY_FIX == 1 + ACT_IDLE_AR2, + ACT_IDLE_ANGRY_AR2, + + ACT_IDLE_AR2_RELAXED, + ACT_IDLE_AR2_STIMULATED, + + ACT_WALK_AR2_RELAXED, + ACT_RUN_AR2_RELAXED, + ACT_WALK_AR2_STIMULATED, + ACT_RUN_AR2_STIMULATED, + + ACT_IDLE_AIM_AR2_STIMULATED, + ACT_WALK_AIM_AR2_STIMULATED, + ACT_RUN_AIM_AR2_STIMULATED, + + ACT_WALK_AR2, + ACT_WALK_AIM_AR2, + ACT_RUN_AR2, + ACT_RUN_AIM_AR2, + + ACT_RELOAD_AR2, + ACT_RELOAD_AR2_LOW, + + ACT_GESTURE_RELOAD_AR2, + + ACT_COVER_AR2_LOW, +#endif // AR2_ACTIVITY_FIX + +#if SHARED_COMBINE_ACTIVITIES + ACT_COMBINE_THROW_GRENADE, + ACT_COMBINE_AR2_ALTFIRE, + + // Gesture versions for existing Combine signal and grenade activities + ACT_GESTURE_COMBINE_THROW_GRENADE, + ACT_GESTURE_COMBINE_AR2_ALTFIRE, + ACT_GESTURE_SPECIAL_ATTACK1, + ACT_GESTURE_SPECIAL_ATTACK2, + + ACT_GESTURE_SIGNAL_ADVANCE, + ACT_GESTURE_SIGNAL_FORWARD, + ACT_GESTURE_SIGNAL_GROUP, + ACT_GESTURE_SIGNAL_HALT, + ACT_GESTURE_SIGNAL_LEFT, + ACT_GESTURE_SIGNAL_RIGHT, + ACT_GESTURE_SIGNAL_TAKECOVER, +#endif // SHARED_COMBINE_ACTIVITIES + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + // Revolver (357) + ACT_IDLE_REVOLVER, + ACT_IDLE_ANGRY_REVOLVER, + ACT_WALK_REVOLVER, + ACT_RUN_REVOLVER, + ACT_WALK_AIM_REVOLVER, + ACT_RUN_AIM_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER, + ACT_RELOAD_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER_LOW, + ACT_RELOAD_REVOLVER_LOW, + ACT_COVER_REVOLVER_LOW, + ACT_RANGE_AIM_REVOLVER_LOW, + ACT_GESTURE_RANGE_ATTACK_REVOLVER, + ACT_GESTURE_RELOAD_REVOLVER, + + // Crossbow + ACT_IDLE_CROSSBOW, + ACT_IDLE_ANGRY_CROSSBOW, + ACT_WALK_CROSSBOW, + ACT_RUN_CROSSBOW, + ACT_WALK_AIM_CROSSBOW, + ACT_RUN_AIM_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW, + ACT_RELOAD_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW_LOW, + ACT_RELOAD_CROSSBOW_LOW, + ACT_COVER_CROSSBOW_LOW, + ACT_RANGE_AIM_CROSSBOW_LOW, + ACT_GESTURE_RANGE_ATTACK_CROSSBOW, + ACT_GESTURE_RELOAD_CROSSBOW, + + ACT_IDLE_CROSSBOW_RELAXED, + ACT_IDLE_CROSSBOW_STIMULATED, + ACT_WALK_CROSSBOW_RELAXED, + ACT_RUN_CROSSBOW_RELAXED, + ACT_WALK_CROSSBOW_STIMULATED, + ACT_RUN_CROSSBOW_STIMULATED, + + ACT_IDLE_AIM_CROSSBOW_STIMULATED, + ACT_WALK_AIM_CROSSBOW_STIMULATED, + ACT_RUN_AIM_CROSSBOW_STIMULATED, + + // Pistol + ACT_IDLE_PISTOL_RELAXED, + ACT_IDLE_PISTOL_STIMULATED, + ACT_WALK_PISTOL_RELAXED, + ACT_WALK_PISTOL_STIMULATED, + ACT_RUN_PISTOL_RELAXED, + ACT_RUN_PISTOL_STIMULATED, + + ACT_IDLE_AIM_PISTOL_STIMULATED, + ACT_WALK_AIM_PISTOL_STIMULATED, + ACT_RUN_AIM_PISTOL_STIMULATED, + + ACT_WALK_CROUCH_PISTOL, + ACT_WALK_CROUCH_AIM_PISTOL, + ACT_RUN_CROUCH_PISTOL, + ACT_RUN_CROUCH_AIM_PISTOL, + + // Shotgun + ACT_IDLE_SHOTGUN, + ACT_WALK_SHOTGUN, + ACT_RUN_SHOTGUN, + + ACT_COVER_SHOTGUN_LOW, + ACT_RANGE_AIM_SHOTGUN_LOW, + + ACT_WALK_SHOTGUN_RELAXED, + ACT_WALK_SHOTGUN_STIMULATED, + ACT_RUN_SHOTGUN_RELAXED, + ACT_RUN_SHOTGUN_STIMULATED, + + ACT_IDLE_AIM_SHOTGUN_STIMULATED, + ACT_WALK_AIM_SHOTGUN_STIMULATED, + ACT_RUN_AIM_SHOTGUN_STIMULATED, + + // RPG + ACT_RANGE_AIM_RPG_LOW, + ACT_RANGE_ATTACK_RPG_LOW, + ACT_GESTURE_RANGE_ATTACK_RPG, + + // Annabelle + ACT_RANGE_ATTACK_ANNABELLE, + ACT_RANGE_ATTACK_ANNABELLE_LOW, + ACT_GESTURE_RANGE_ATTACK_ANNABELLE, + ACT_RELOAD_ANNABELLE, + ACT_RELOAD_ANNABELLE_LOW, + ACT_GESTURE_RELOAD_ANNABELLE, + + // Melee + ACT_WALK_MELEE, + ACT_RUN_MELEE, + + // Citizen accessories + ACT_RUN_PACKAGE, + ACT_RUN_SUITCASE, + + // Holster/Unholster + ACT_ARM_RIFLE, + ACT_ARM_SHOTGUN, + ACT_ARM_RPG, + ACT_ARM_MELEE, + ACT_DISARM_RIFLE, + ACT_DISARM_SHOTGUN, + ACT_DISARM_RPG, + ACT_DISARM_MELEE, +#endif // EXPANDED_HL2_WEAPON_ACTIVITIES + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // AR1 + ACT_IDLE_AR1, + ACT_IDLE_ANGRY_AR1, + ACT_WALK_AR1, + ACT_RUN_AR1, + ACT_WALK_AIM_AR1, + ACT_RUN_AIM_AR1, + //ACT_RANGE_ATTACK_AR1, + ACT_RELOAD_AR1, + ACT_RANGE_ATTACK_AR1_LOW, + ACT_RELOAD_AR1_LOW, + ACT_COVER_AR1_LOW, + ACT_RANGE_AIM_AR1_LOW, + //ACT_GESTURE_RANGE_ATTACK_AR1, + ACT_GESTURE_RELOAD_AR1, + + ACT_IDLE_AR1_RELAXED, + ACT_IDLE_AR1_STIMULATED, + ACT_WALK_AR1_RELAXED, + ACT_RUN_AR1_RELAXED, + ACT_WALK_AR1_STIMULATED, + ACT_RUN_AR1_STIMULATED, + + ACT_IDLE_AIM_AR1_STIMULATED, + ACT_WALK_AIM_AR1_STIMULATED, + ACT_RUN_AIM_AR1_STIMULATED, + + // AR3 (new) + ACT_IDLE_AR3, + ACT_IDLE_ANGRY_AR3, + ACT_WALK_AR3, + ACT_RUN_AR3, + ACT_WALK_AIM_AR3, + ACT_RUN_AIM_AR3, + ACT_RANGE_ATTACK_AR3, + ACT_RELOAD_AR3, + ACT_RANGE_ATTACK_AR3_LOW, + ACT_RELOAD_AR3_LOW, + ACT_COVER_AR3_LOW, + ACT_RANGE_AIM_AR3_LOW, + ACT_GESTURE_RANGE_ATTACK_AR3, + ACT_GESTURE_RELOAD_AR3, + + ACT_IDLE_AR3_RELAXED, + ACT_IDLE_AR3_STIMULATED, + ACT_WALK_AR3_RELAXED, + ACT_RUN_AR3_RELAXED, + ACT_WALK_AR3_STIMULATED, + ACT_RUN_AR3_STIMULATED, + + ACT_IDLE_AIM_AR3_STIMULATED, + ACT_WALK_AIM_AR3_STIMULATED, + ACT_RUN_AIM_AR3_STIMULATED, + + // SMG2 + ACT_IDLE_SMG2, + ACT_IDLE_ANGRY_SMG2, + ACT_WALK_SMG2, + ACT_RUN_SMG2, + ACT_WALK_AIM_SMG2, + ACT_RUN_AIM_SMG2, + //ACT_RANGE_ATTACK_SMG2, + ACT_RELOAD_SMG2, + ACT_RANGE_ATTACK_SMG2_LOW, + ACT_RELOAD_SMG2_LOW, + ACT_COVER_SMG2_LOW, + ACT_RANGE_AIM_SMG2_LOW, + //ACT_GESTURE_RANGE_ATTACK_SMG2, + ACT_GESTURE_RELOAD_SMG2, + + ACT_IDLE_SMG2_RELAXED, + ACT_IDLE_SMG2_STIMULATED, + ACT_WALK_SMG2_RELAXED, + ACT_RUN_SMG2_RELAXED, + ACT_WALK_SMG2_STIMULATED, + ACT_RUN_SMG2_STIMULATED, + + ACT_IDLE_AIM_SMG2_STIMULATED, + ACT_WALK_AIM_SMG2_STIMULATED, + ACT_RUN_AIM_SMG2_STIMULATED, + + // SMG3 (new) + ACT_IDLE_SMG3, + ACT_IDLE_ANGRY_SMG3, + ACT_WALK_SMG3, + ACT_RUN_SMG3, + ACT_WALK_AIM_SMG3, + ACT_RUN_AIM_SMG3, + ACT_RANGE_ATTACK_SMG3, + ACT_RELOAD_SMG3, + ACT_RANGE_ATTACK_SMG3_LOW, + ACT_RELOAD_SMG3_LOW, + ACT_COVER_SMG3_LOW, + ACT_RANGE_AIM_SMG3_LOW, + ACT_GESTURE_RANGE_ATTACK_SMG3, + ACT_GESTURE_RELOAD_SMG3, + + ACT_IDLE_SMG3_RELAXED, + ACT_IDLE_SMG3_STIMULATED, + ACT_WALK_SMG3_RELAXED, + ACT_RUN_SMG3_RELAXED, + ACT_WALK_SMG3_STIMULATED, + ACT_RUN_SMG3_STIMULATED, + + ACT_IDLE_AIM_SMG3_STIMULATED, + ACT_WALK_AIM_SMG3_STIMULATED, + ACT_RUN_AIM_SMG3_STIMULATED, + + // HMG1 + ACT_IDLE_HMG1, + ACT_IDLE_ANGRY_HMG1, + ACT_WALK_HMG1, + ACT_RUN_HMG1, + ACT_WALK_AIM_HMG1, + ACT_RUN_AIM_HMG1, + //ACT_RANGE_ATTACK_HMG1, + ACT_RELOAD_HMG1, + ACT_RANGE_ATTACK_HMG1_LOW, + ACT_RELOAD_HMG1_LOW, + ACT_COVER_HMG1_LOW, + ACT_RANGE_AIM_HMG1_LOW, + //ACT_GESTURE_RANGE_ATTACK_HMG1, + ACT_GESTURE_RELOAD_HMG1, + + ACT_IDLE_HMG1_RELAXED, + ACT_IDLE_HMG1_STIMULATED, + ACT_WALK_HMG1_RELAXED, + ACT_RUN_HMG1_RELAXED, + ACT_WALK_HMG1_STIMULATED, + ACT_RUN_HMG1_STIMULATED, + + ACT_IDLE_AIM_HMG1_STIMULATED, + ACT_WALK_AIM_HMG1_STIMULATED, + ACT_RUN_AIM_HMG1_STIMULATED, + + // Sniper Rifle + ACT_IDLE_SNIPER_RIFLE, + ACT_IDLE_ANGRY_SNIPER_RIFLE, + ACT_WALK_SNIPER_RIFLE, + ACT_RUN_SNIPER_RIFLE, + ACT_WALK_AIM_SNIPER_RIFLE, + ACT_RUN_AIM_SNIPER_RIFLE, + //ACT_RANGE_ATTACK_SNIPER_RIFLE, + ACT_RELOAD_SNIPER_RIFLE, + ACT_RANGE_ATTACK_SNIPER_RIFLE_LOW, + ACT_RELOAD_SNIPER_RIFLE_LOW, + ACT_COVER_SNIPER_RIFLE_LOW, + ACT_RANGE_AIM_SNIPER_RIFLE_LOW, + //ACT_GESTURE_RANGE_ATTACK_SNIPER_RIFLE, + ACT_GESTURE_RELOAD_SNIPER_RIFLE, + + ACT_IDLE_SNIPER_RIFLE_RELAXED, + ACT_IDLE_SNIPER_RIFLE_STIMULATED, + ACT_WALK_SNIPER_RIFLE_RELAXED, + ACT_RUN_SNIPER_RIFLE_RELAXED, + ACT_WALK_SNIPER_RIFLE_STIMULATED, + ACT_RUN_SNIPER_RIFLE_STIMULATED, + + ACT_IDLE_AIM_SNIPER_RIFLE_STIMULATED, + ACT_WALK_AIM_SNIPER_RIFLE_STIMULATED, + ACT_RUN_AIM_SNIPER_RIFLE_STIMULATED, + + // Dual Pistols + ACT_IDLE_DUAL_PISTOLS, + ACT_IDLE_ANGRY_DUAL_PISTOLS, + ACT_WALK_DUAL_PISTOLS, + ACT_RUN_DUAL_PISTOLS, + ACT_WALK_AIM_DUAL_PISTOLS, + ACT_RUN_AIM_DUAL_PISTOLS, + ACT_RANGE_ATTACK_DUAL_PISTOLS, + ACT_RELOAD_DUAL_PISTOLS, + ACT_RANGE_ATTACK_DUAL_PISTOLS_LOW, + ACT_RELOAD_DUAL_PISTOLS_LOW, + ACT_COVER_DUAL_PISTOLS_LOW, + ACT_RANGE_AIM_DUAL_PISTOLS_LOW, + ACT_GESTURE_RANGE_ATTACK_DUAL_PISTOLS, + ACT_GESTURE_RELOAD_DUAL_PISTOLS, + + ACT_IDLE_DUAL_PISTOLS_RELAXED, + ACT_IDLE_DUAL_PISTOLS_STIMULATED, + ACT_WALK_DUAL_PISTOLS_RELAXED, + ACT_RUN_DUAL_PISTOLS_RELAXED, + ACT_WALK_DUAL_PISTOLS_STIMULATED, + ACT_RUN_DUAL_PISTOLS_STIMULATED, + + ACT_IDLE_AIM_DUAL_PISTOLS_STIMULATED, + ACT_WALK_AIM_DUAL_PISTOLS_STIMULATED, + ACT_RUN_AIM_DUAL_PISTOLS_STIMULATED, +#endif // EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + +#if EXPANDED_NAVIGATION_ACTIVITIES + ACT_CLIMB_ALL, // An actual blend animation which uses pose parameters for direction + ACT_CLIMB_IDLE, + + ACT_CLIMB_MOUNT_TOP, + ACT_CLIMB_MOUNT_BOTTOM, + ACT_CLIMB_DISMOUNT_BOTTOM, +#endif // EXPANDED_NAVIGATION_ACTIVITIES + +#if EXPANDED_HL2_COVER_ACTIVITIES + // Crouch Cover Medium + ACT_RANGE_ATTACK1_MED, + ACT_RANGE_ATTACK2_MED, + ACT_RANGE_AIM_MED, + + ACT_RANGE_ATTACK_AR2_MED, + ACT_RANGE_ATTACK_SMG1_MED, + ACT_RANGE_ATTACK_SHOTGUN_MED, + ACT_RANGE_ATTACK_PISTOL_MED, + ACT_RANGE_ATTACK_RPG_MED, + ACT_RANGE_ATTACK_REVOLVER_MED, + ACT_RANGE_ATTACK_CROSSBOW_MED, + + ACT_RANGE_AIM_AR2_MED, + ACT_RANGE_AIM_SMG1_MED, + ACT_RANGE_AIM_SHOTGUN_MED, + ACT_RANGE_AIM_PISTOL_MED, + ACT_RANGE_AIM_RPG_MED, + ACT_RANGE_AIM_REVOLVER_MED, + ACT_RANGE_AIM_CROSSBOW_MED, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // MED activities for unused weapons + ACT_RANGE_AIM_AR1_MED, + ACT_RANGE_ATTACK_AR1_MED, + ACT_RANGE_AIM_AR3_MED, + ACT_RANGE_ATTACK_AR3_MED, + ACT_RANGE_AIM_SMG2_MED, + ACT_RANGE_ATTACK_SMG2_MED, + ACT_RANGE_AIM_SMG3_MED, + ACT_RANGE_ATTACK_SMG3_MED, + ACT_RANGE_AIM_HMG1_MED, + ACT_RANGE_ATTACK_HMG1_MED, + ACT_RANGE_AIM_SNIPER_RIFLE_MED, + ACT_RANGE_ATTACK_SNIPER_RIFLE_MED, + ACT_RANGE_AIM_DUAL_PISTOLS_MED, + ACT_RANGE_ATTACK_DUAL_PISTOLS_MED, +#endif // EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + + // Wall Cover (for use in custom cover hints) + ACT_COVER_WALL_R, + ACT_COVER_WALL_L, + ACT_COVER_WALL_LOW_R, + ACT_COVER_WALL_LOW_L, + + ACT_COVER_WALL_R_RIFLE, + ACT_COVER_WALL_L_RIFLE, + ACT_COVER_WALL_LOW_R_RIFLE, + ACT_COVER_WALL_LOW_L_RIFLE, + + ACT_COVER_WALL_R_PISTOL, + ACT_COVER_WALL_L_PISTOL, + ACT_COVER_WALL_LOW_R_PISTOL, + ACT_COVER_WALL_LOW_L_PISTOL, +#endif // EXPANDED_HL2_COVER_ACTIVITIES + +#if EXPANDED_HL2DM_ACTIVITIES + ACT_HL2MP_WALK, + ACT_HL2MP_WALK_PISTOL, + ACT_HL2MP_WALK_SHOTGUN, + ACT_HL2MP_WALK_SMG1, + ACT_HL2MP_WALK_AR2, + ACT_HL2MP_WALK_PHYSGUN, + ACT_HL2MP_WALK_GRENADE, + ACT_HL2MP_WALK_RPG, + ACT_HL2MP_WALK_CROSSBOW, + ACT_HL2MP_WALK_MELEE, + ACT_HL2MP_WALK_SLAM, + + ACT_HL2MP_GESTURE_RANGE_ATTACK2, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_PHYSGUN, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SLAM, + + ACT_HL2MP_IDLE_REVOLVER, + ACT_HL2MP_RUN_REVOLVER, + ACT_HL2MP_WALK_REVOLVER, + ACT_HL2MP_IDLE_CROUCH_REVOLVER, + ACT_HL2MP_WALK_CROUCH_REVOLVER, + ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER, + ACT_HL2MP_GESTURE_RELOAD_REVOLVER, + ACT_HL2MP_JUMP_REVOLVER, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Player activities for unused weapons + ACT_HL2MP_IDLE_AR1, + ACT_HL2MP_RUN_AR1, + ACT_HL2MP_WALK_AR1, + ACT_HL2MP_IDLE_CROUCH_AR1, + ACT_HL2MP_WALK_CROUCH_AR1, + ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1, + ACT_HL2MP_GESTURE_RELOAD_AR1, + ACT_HL2MP_JUMP_AR1, + + ACT_HL2MP_IDLE_AR3, + ACT_HL2MP_RUN_AR3, + ACT_HL2MP_WALK_AR3, + ACT_HL2MP_IDLE_CROUCH_AR3, + ACT_HL2MP_WALK_CROUCH_AR3, + ACT_HL2MP_GESTURE_RANGE_ATTACK_AR3, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR3, + ACT_HL2MP_GESTURE_RELOAD_AR3, + ACT_HL2MP_JUMP_AR3, + + ACT_HL2MP_IDLE_SMG2, + ACT_HL2MP_RUN_SMG2, + ACT_HL2MP_WALK_SMG2, + ACT_HL2MP_IDLE_CROUCH_SMG2, + ACT_HL2MP_WALK_CROUCH_SMG2, + ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG2, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG2, + ACT_HL2MP_GESTURE_RELOAD_SMG2, + ACT_HL2MP_JUMP_SMG2, + + ACT_HL2MP_IDLE_SMG3, + ACT_HL2MP_RUN_SMG3, + ACT_HL2MP_WALK_SMG3, + ACT_HL2MP_IDLE_CROUCH_SMG3, + ACT_HL2MP_WALK_CROUCH_SMG3, + ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG3, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG3, + ACT_HL2MP_GESTURE_RELOAD_SMG3, + ACT_HL2MP_JUMP_SMG3, + + ACT_HL2MP_IDLE_HMG1, + ACT_HL2MP_RUN_HMG1, + ACT_HL2MP_WALK_HMG1, + ACT_HL2MP_IDLE_CROUCH_HMG1, + ACT_HL2MP_WALK_CROUCH_HMG1, + ACT_HL2MP_GESTURE_RANGE_ATTACK_HMG1, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_HMG1, + ACT_HL2MP_GESTURE_RELOAD_HMG1, + ACT_HL2MP_JUMP_HMG1, + + ACT_HL2MP_IDLE_SNIPER_RIFLE, + ACT_HL2MP_RUN_SNIPER_RIFLE, + ACT_HL2MP_WALK_SNIPER_RIFLE, + ACT_HL2MP_IDLE_CROUCH_SNIPER_RIFLE, + ACT_HL2MP_WALK_CROUCH_SNIPER_RIFLE, + ACT_HL2MP_GESTURE_RANGE_ATTACK_SNIPER_RIFLE, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_SNIPER_RIFLE, + ACT_HL2MP_GESTURE_RELOAD_SNIPER_RIFLE, + ACT_HL2MP_JUMP_SNIPER_RIFLE, + + ACT_HL2MP_IDLE_DUAL_PISTOLS, + ACT_HL2MP_RUN_DUAL_PISTOLS, + ACT_HL2MP_WALK_DUAL_PISTOLS, + ACT_HL2MP_IDLE_CROUCH_DUAL_PISTOLS, + ACT_HL2MP_WALK_CROUCH_DUAL_PISTOLS, + ACT_HL2MP_GESTURE_RANGE_ATTACK_DUAL_PISTOLS, + ACT_HL2MP_GESTURE_RANGE_ATTACK2_DUAL_PISTOLS, + ACT_HL2MP_GESTURE_RELOAD_DUAL_PISTOLS, + ACT_HL2MP_JUMP_DUAL_PISTOLS, +#endif // EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + + ACT_HL2MP_IDLE_USE, + ACT_HL2MP_RUN_USE, + ACT_HL2MP_WALK_USE, + ACT_HL2MP_IDLE_CROUCH_USE, + ACT_HL2MP_WALK_CROUCH_USE, + ACT_HL2MP_JUMP_USE, + + ACT_HL2MP_IDLE_USE_HEAVY, + ACT_HL2MP_RUN_USE_HEAVY, + ACT_HL2MP_WALK_USE_HEAVY, + ACT_HL2MP_IDLE_CROUCH_USE_HEAVY, + ACT_HL2MP_WALK_CROUCH_USE_HEAVY, + ACT_HL2MP_JUMP_USE_HEAVY, +#endif // EXPANDED_HL2DM_ACTIVITIES + // this is the end of the global activities, private per-monster activities start here. LAST_SHARED_ACTIVITY, } Activity; - #endif // AI_ACTIVITY_H - diff --git a/game/shared/ai_criteria_new.cpp b/game/shared/ai_criteria_new.cpp new file mode 100644 index 000000000..7d96e020b --- /dev/null +++ b/game/shared/ai_criteria_new.cpp @@ -0,0 +1,37 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "cbase.h" +#include "AI_Criteria.h" + +#ifdef GAME_DLL +#include "ai_speech.h" +#endif + +#include +#include "engine/IEngineSound.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + + + +BEGIN_SIMPLE_DATADESC( AI_ResponseParams ) + DEFINE_FIELD( flags, FIELD_SHORT ), + DEFINE_FIELD( odds, FIELD_SHORT ), + DEFINE_FIELD( soundlevel, FIELD_CHARACTER ), + DEFINE_FIELD( delay, FIELD_INTEGER ), // These are compressed down to two float16s, so treat as an INT for saverestore + DEFINE_FIELD( respeakdelay, FIELD_INTEGER ), // +END_DATADESC() + +BEGIN_SIMPLE_DATADESC( AI_Response ) + DEFINE_FIELD( m_Type, FIELD_CHARACTER ), + DEFINE_ARRAY( m_szResponseName, FIELD_CHARACTER, AI_Response::MAX_RESPONSE_NAME ), + DEFINE_ARRAY( m_szMatchingRule, FIELD_CHARACTER, AI_Response::MAX_RULE_NAME ), + // DEFINE_FIELD( m_pCriteria, FIELD_??? ), // Don't need to save this probably + DEFINE_EMBEDDED( m_Params ), +END_DATADESC() diff --git a/game/shared/ai_criteria_new.h b/game/shared/ai_criteria_new.h new file mode 100644 index 000000000..84d749c59 --- /dev/null +++ b/game/shared/ai_criteria_new.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef AI_CRITERIA_H +#define AI_CRITERIA_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier1/interval.h" +#include "mathlib/compressed_vector.h" +#include "../../public/responserules/response_types.h" + + +using ResponseRules::ResponseType_t; + +extern const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration, const char *entireContext ); + +#ifndef AI_CriteriaSet +#define AI_CriteriaSet ResponseRules::CriteriaSet +#endif + +typedef ResponseRules::ResponseParams AI_ResponseParams ; +typedef ResponseRules::CRR_Response AI_Response; + +#endif // AI_CRITERIA_H diff --git a/game/shared/ai_speechconcept.cpp b/game/shared/ai_speechconcept.cpp new file mode 100644 index 000000000..a1b992fca --- /dev/null +++ b/game/shared/ai_speechconcept.cpp @@ -0,0 +1,28 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "ai_speechconcept.h" + +#ifdef GAME_DLL +#include "game.h" +#include "ai_basenpc.h" +#include "sceneentity.h" +#endif // GAME_DLL + +#include "engine/ienginesound.h" +#include "keyvalues.h" +#include "ai_criteria.h" +#include "isaverestore.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include + + +// empty diff --git a/game/shared/ai_speechconcept.h b/game/shared/ai_speechconcept.h new file mode 100644 index 000000000..5701ee313 --- /dev/null +++ b/game/shared/ai_speechconcept.h @@ -0,0 +1,44 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Class data for an AI Concept, an atom of response-driven dialog. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_SPEECHCONCEPT_H +#define AI_SPEECHCONCEPT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "responserules/response_types.h" + +class CAI_Concept : public ResponseRules::CRR_Concept +{ +public: + CAI_Concept() {}; + // construct concept from a string. + CAI_Concept(const char *fromString) : CRR_Concept(fromString) {} ; + + // get/set BS + inline EHANDLE GetSpeaker() const { return m_hSpeaker; } + inline void SetSpeaker(EHANDLE val) { m_hSpeaker = val; } + + /* + inline EHANDLE GetTarget() const { return m_hTarget; } + inline void SetTarget(EHANDLE val) { m_hTarget = val; } + inline EHANDLE GetTopic() const { return m_hTopic; } + inline void SetTopic(EHANDLE val) { m_hTopic = val; } + */ + +protected: + EHANDLE m_hSpeaker; + + /* + EHANDLE m_hTarget; + EHANDLE m_hTopic; + */ +}; + +#endif // AI_SPEECHCONCEPT_H diff --git a/game/shared/ammodef.cpp b/game/shared/ammodef.cpp index 257e8569a..bb7ca28c9 100644 --- a/game/shared/ammodef.cpp +++ b/game/shared/ammodef.cpp @@ -24,6 +24,21 @@ Ammo_t *CAmmoDef::GetAmmoOfIndex(int nAmmoIndex) return &m_AmmoType[ nAmmoIndex ]; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +const char* CAmmoDef::Name(int nAmmoIndex) +{ + if ( nAmmoIndex < 1 || nAmmoIndex >= m_nAmmoIndex ) + return NULL; + + return m_AmmoType[nAmmoIndex].pName; +} +#endif // MAPBASE + //----------------------------------------------------------------------------- // Purpose: // Input : @@ -292,4 +307,22 @@ CAmmoDef::~CAmmoDef( void ) } } - +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAmmoDef, SCRIPT_SINGLETON "The ammo type definition manager." ) + + DEFINE_SCRIPTFUNC( Name, "Gets the name of the specified ammo type index." ) + DEFINE_SCRIPTFUNC( Index, "Gets the index of the specified ammo type name." ) + DEFINE_SCRIPTFUNC( PlrDamage, "Gets the damage players deal for the specified ammo type." ) + DEFINE_SCRIPTFUNC( NPCDamage, "Gets the damage NPCs deal for the specified ammo type." ) + DEFINE_SCRIPTFUNC( MaxCarry, "Gets the maximum amount of this ammo type which players should be able to carry." ) + DEFINE_SCRIPTFUNC( DamageType, "Gets the type of damage this ammo type deals." ) + DEFINE_SCRIPTFUNC( TracerType, "Gets the type of tracer this ammo type uses." ) + DEFINE_SCRIPTFUNC( DamageForce, "Gets the amount of force this ammo type deals." ) + DEFINE_SCRIPTFUNC( MinSplashSize, "Gets the minimum size of water splashes caused by impacts from this ammo type." ) + DEFINE_SCRIPTFUNC( MaxSplashSize, "Gets the maximum size of water splashes caused by impacts from this ammo type." ) + DEFINE_SCRIPTFUNC( Flags, "Gets the flags this ammo type uses." ) + + DEFINE_SCRIPTFUNC( GetNumAmmoTypes, "Gets the number of ammo types which currently exist." ) + +END_SCRIPTDESC(); +#endif // MAPBASE_VSCRIPT diff --git a/game/shared/ammodef.h b/game/shared/ammodef.h index 71c1707f2..5e62158ad 100644 --- a/game/shared/ammodef.h +++ b/game/shared/ammodef.h @@ -72,6 +72,9 @@ class CAmmoDef Ammo_t m_AmmoType[MAX_AMMO_TYPES]; Ammo_t *GetAmmoOfIndex(int nAmmoIndex); +#ifdef MAPBASE + const char* Name(int nAmmoIndex); +#endif // MAPBASE int Index(const char *psz); int PlrDamage(int nAmmoIndex); int NPCDamage(int nAmmoIndex); @@ -91,8 +94,12 @@ class CAmmoDef private: bool AddAmmoType(char const* name, int damageType, int tracerType, int nFlags, int minSplashSize, int maxSplashSize ); -}; +#ifdef MAPBASE_VSCRIPT + ALLOW_SCRIPT_ACCESS(); + int GetNumAmmoTypes() { return m_nAmmoIndex; } +#endif // MAPBASE_VSCRIPT +}; // Get the global ammodef object. This is usually implemented in each mod's game rules file somewhere, // so the mod can setup custom ammo types. diff --git a/game/shared/base_playeranimstate.cpp b/game/shared/base_playeranimstate.cpp index d90655ac5..2aee54e03 100644 --- a/game/shared/base_playeranimstate.cpp +++ b/game/shared/base_playeranimstate.cpp @@ -539,7 +539,26 @@ bool CBasePlayerAnimState::CanThePlayerMove() void CBasePlayerAnimState::ComputePlaybackRate() { VPROF( "CBasePlayerAnimState::ComputePlaybackRate" ); +#ifdef MAPBASE + if ( m_AnimConfig.m_LegAnimType == LEGANIM_9WAY ) + { + // If the movement would be greater than the pose range, set playback rate anyway + if ( abs(m_vLastMovePose.x) > 1.0f || abs(m_vLastMovePose.y) > 1.0f ) + { + bool bIsMoving; + float flRate = CalcMovementPlaybackRate( &bIsMoving ); + if ( bIsMoving ) + GetOuter()->SetPlaybackRate( flRate ); + else + GetOuter()->SetPlaybackRate( 1 ); + } + else + GetOuter()->SetPlaybackRate( 1 ); + } + else // Allow LEGANIM_8WAY to change playback rate +#else if ( m_AnimConfig.m_LegAnimType != LEGANIM_9WAY && m_AnimConfig.m_LegAnimType != LEGANIM_8WAY ) +#endif // MAPBASE { // When using a 9-way blend, playback rate is always 1 and we just scale the pose params // to speed up or slow down the animation. diff --git a/game/shared/basecombatcharacter_shared.cpp b/game/shared/basecombatcharacter_shared.cpp index c032cd547..a27d2b3e5 100644 --- a/game/shared/basecombatcharacter_shared.cpp +++ b/game/shared/basecombatcharacter_shared.cpp @@ -82,7 +82,11 @@ bool CBaseCombatCharacter::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) return false; } +#ifdef MAPBASE + if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) && !pWeapon->HasSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY) ) +#else if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) ) +#endif // MAPBASE return false; if ( !pWeapon->CanDeploy() ) @@ -208,6 +212,16 @@ void CBaseCombatCharacter::SetBloodColor( int nBloodColor ) m_bloodColor = nBloodColor; } +#if defined(MAPBASE) && defined(GAME_DLL) +//----------------------------------------------------------------------------- +// Purpose: Sets blood color +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::InputSetBloodColor( inputdata_t &inputdata ) +{ + SetBloodColor(inputdata.value.Int()); +} +#endif + //----------------------------------------------------------------------------- /** The main visibility check. Checks all the entity specific reasons that could diff --git a/game/shared/basecombatweapon_shared.cpp b/game/shared/basecombatweapon_shared.cpp index 0b35d1470..a65be7b23 100644 --- a/game/shared/basecombatweapon_shared.cpp +++ b/game/shared/basecombatweapon_shared.cpp @@ -179,11 +179,20 @@ void CBaseCombatWeapon::Spawn( void ) // Assume m_nViewModelIndex = 0; +#ifdef MAPBASE + // Don't reset to default ammo if we're supposed to use the keyvalue + if (!HasSpawnFlags( SF_WEAPON_PRESERVE_AMMO )) +#endif // MAPBASE + GiveDefaultAmmo(); if ( GetWorldModel() ) { +#ifdef MAPBASE + SetModel( (GetDroppedModel() && GetDroppedModel()[0]) ? GetDroppedModel() : GetWorldModel() ); +#else SetModel( GetWorldModel() ); +#endif // MAPBASE } #if !defined( CLIENT_DLL ) @@ -239,7 +248,13 @@ void CBaseCombatWeapon::Precache( void ) // Add this weapon to the weapon registry, and get our index into it // Get weapon data from script file +#ifdef MAPBASE + // Allow custom scripts to be loaded on a map-by-map basis + if ( ReadCustomWeaponDataFromFileForSlot( filesystem, GetWeaponScriptName(), &m_hWeaponFileInfo, GetEncryptionKey() ) || + ReadWeaponDataFromFileForSlot( filesystem, GetWeaponScriptName(), &m_hWeaponFileInfo, GetEncryptionKey() ) ) +#else if ( ReadWeaponDataFromFileForSlot( filesystem, GetClassname(), &m_hWeaponFileInfo, GetEncryptionKey() ) ) +#endif // MAPBASE { // Get the ammo indexes for the ammo's specified in the data file if ( GetWpnData().szAmmo1[0] ) @@ -283,6 +298,19 @@ void CBaseCombatWeapon::Precache( void ) m_iWorldModelIndex = CBaseEntity::PrecacheModel( GetWorldModel() ); } +#ifdef MAPBASE + m_iDroppedModelIndex = 0; + if ( GetDroppedModel() && GetDroppedModel()[0] ) + { + m_iDroppedModelIndex = CBaseEntity::PrecacheModel( GetDroppedModel() ); + } + else + { + // Use the world model index + m_iDroppedModelIndex = m_iWorldModelIndex; + } +#endif // MAPBASE + // Precache sounds, too for ( int i = 0; i < NUM_SHOOT_SOUND_TYPES; ++i ) { diff --git a/public/studio.cpp b/public/studio.cpp index cce662202..19c94a7f2 100644 --- a/public/studio.cpp +++ b/public/studio.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -20,7 +20,7 @@ //----------------------------------------------------------------------------- mstudioanimdesc_t &studiohdr_t::pAnimdesc( int i ) const -{ +{ if (numincludemodels == 0) { return *pLocalAnimdesc( i ); @@ -189,7 +189,7 @@ mstudioikrule_t *mstudioanimdesc_t::pIKRule( int i ) const else { byte *pAnimBlocks = pStudiohdr()->GetAnimBlock( animblock ); - + if ( pAnimBlocks ) { return (mstudioikrule_t *)(pAnimBlocks + animblockikruleindex) + i; @@ -212,7 +212,7 @@ mstudiolocalhierarchy_t *mstudioanimdesc_t::pHierarchy( int i ) const else { byte *pAnimBlocks = pStudiohdr()->GetAnimBlock( animblock ); - + if ( pAnimBlocks ) { return (mstudiolocalhierarchy_t *)(pAnimBlocks + localhierarchyindex) + i; @@ -674,7 +674,7 @@ int studiohdr_t::CopyAutoplaySequences( unsigned short *pOut, int outCount ) con // Purpose: maps local sequence bone to global bone //----------------------------------------------------------------------------- -int studiohdr_t::RemapSeqBone( int iSequence, int iLocalBone ) const +int studiohdr_t::RemapSeqBone( int iSequence, int iLocalBone ) const { // remap bone virtualmodel_t *pVModel = GetVirtualModel(); @@ -707,7 +707,7 @@ int studiohdr_t::RemapAnimBone( int iAnim, int iLocalBone ) const // Purpose: //----------------------------------------------------------------------------- -CStudioHdr::CStudioHdr( void ) +CStudioHdr::CStudioHdr( void ) { // set pointer to bogus value m_nFrameUnlockCounter = 0; @@ -715,7 +715,7 @@ CStudioHdr::CStudioHdr( void ) Init( NULL ); } -CStudioHdr::CStudioHdr( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache ) +CStudioHdr::CStudioHdr( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache ) { // preset pointer to bogus value (it may be overwritten with legitimate data later) m_nFrameUnlockCounter = 0; @@ -807,7 +807,7 @@ const virtualmodel_t * CStudioHdr::ResetVModel( const virtualmodel_t *pVModel ) { m_pStudioHdrCache[ i ] = NULL; } - + return const_cast(pVModel); } else @@ -886,7 +886,7 @@ const studiohdr_t *CStudioHdr::pAnimStudioHdr( int animation ) mstudioanimdesc_t &CStudioHdr::pAnimdesc( int i ) -{ +{ if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalAnimdesc( i ); @@ -933,7 +933,7 @@ mstudioseqdesc_t &CStudioHdr::pSeqdesc( int i ) // Avoid reading random memory. i = 0; } - + if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalSeqdesc( i ); @@ -1163,7 +1163,7 @@ void CStudioHdr::SetAttachmentBone( int iAttachment, int iBone ) // Purpose: //----------------------------------------------------------------------------- -char *CStudioHdr::pszNodeName( int iNode ) +const char *CStudioHdr::pszNodeName( int iNode ) { if (m_pVModel == NULL) { @@ -1174,7 +1174,7 @@ char *CStudioHdr::pszNodeName( int iNode ) return "Invalid node"; const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_node[iNode-1].group ); - + return pStudioHdr->pszLocalNodeName( m_pVModel->m_node[iNode-1].index ); } @@ -1211,22 +1211,22 @@ int CStudioHdr::GetActivityListVersion( void ) return m_pStudioHdr->activitylistversion; } - int version = m_pStudioHdr->activitylistversion; + int versionLocl = m_pStudioHdr->activitylistversion; int i; for (i = 1; i < m_pVModel->m_group.Count(); i++) { const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); Assert( pStudioHdr ); - version = min( version, pStudioHdr->activitylistversion ); + versionLocl = min( versionLocl, pStudioHdr->activitylistversion ); } - return version; + return versionLocl; } -void CStudioHdr::SetActivityListVersion( int version ) +void CStudioHdr::SetActivityListVersion( int iVersion ) { - m_pStudioHdr->activitylistversion = version; + m_pStudioHdr->activitylistversion = iVersion; if (m_pVModel == NULL) { @@ -1238,7 +1238,7 @@ void CStudioHdr::SetActivityListVersion( int version ) { const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); Assert( pStudioHdr ); - pStudioHdr->SetActivityListVersion( version ); + pStudioHdr->SetActivityListVersion( iVersion ); } } @@ -1348,7 +1348,7 @@ int CStudioHdr::CopyAutoplaySequences( unsigned short *pOut, int outCount ) cons // Purpose: maps local sequence bone to global bone //----------------------------------------------------------------------------- -int CStudioHdr::RemapSeqBone( int iSequence, int iLocalBone ) const +int CStudioHdr::RemapSeqBone( int iSequence, int iLocalBone ) const { // remap bone if (m_pVModel) @@ -1375,7 +1375,7 @@ int CStudioHdr::RemapAnimBone( int iAnim, int iLocalBone ) const //----------------------------------------------------------------------------- -// Purpose: run the interpreted FAC's expressions, converting flex_controller +// Purpose: run the interpreted FAC's expressions, converting flex_controller // values into FAC weights //----------------------------------------------------------------------------- void CStudioHdr::RunFlexRules( const float *src, float *dest ) @@ -1422,17 +1422,17 @@ void CStudioHdr::RunFlexRules( const float *src, float *dest ) { stack[k-2] = 0; } - k--; + k--; break; case STUDIO_NEG: stack[k-1] = -stack[k-1]; break; case STUDIO_MAX: stack[k-2] = max( stack[k-2], stack[k-1] ); k--; break; case STUDIO_MIN: stack[k-2] = min( stack[k-2], stack[k-1] ); k--; break; case STUDIO_CONST: stack[k] = pops->d.value; k++; break; - case STUDIO_FETCH1: - { + case STUDIO_FETCH1: + { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index)->localToGlobal; stack[k] = src[m]; - k++; + k++; break; } case STUDIO_FETCH2: @@ -1464,17 +1464,17 @@ void CStudioHdr::RunFlexRules( const float *src, float *dest ) } break; case STUDIO_2WAY_0: - { + { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; stack[ k ] = RemapValClamped( src[m], -1.0f, 0.0f, 1.0f, 0.0f ); - k++; + k++; } break; case STUDIO_2WAY_1: - { + { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; stack[ k ] = RemapValClamped( src[m], 0.0f, 1.0f, 0.0f, 1.0f ); - k++; + k++; } break; case STUDIO_NWAY: @@ -1506,11 +1506,11 @@ void CStudioHdr::RunFlexRules( const float *src, float *dest ) stack[ k - 5 ] = flValue * src[ v ]; - k -= 4; + k -= 4; } break; case STUDIO_DME_LOWER_EYELID: - { + { const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); @@ -1545,7 +1545,7 @@ void CStudioHdr::RunFlexRules( const float *src, float *dest ) } break; case STUDIO_DME_UPPER_EYELID: - { + { const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); @@ -1620,7 +1620,7 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict { // Algorithm: walk through every sequence in the model, determine to which activity // it corresponds, and keep a count of sequences per activity. Once the total count - // is available, allocate an array large enough to contain them all, update the + // is available, allocate an array large enough to contain them all, update the // starting indices for every activity's section in the array, and go back through, // populating the array with its data. @@ -1636,10 +1636,10 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict #if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE m_bIsInitialized = true; #endif - + // Some studio headers have no activities at all. In those // cases we can avoid a lot of this effort. - bool bFoundOne = false; + bool bFoundOne = false; // for each sequence in the header... const int NumSeq = pstudiohdr->GetNumSeq(); @@ -1663,7 +1663,7 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict HashValueType entry(seqdesc.activity, 0, 1, iabs(seqdesc.actweight)); UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); if ( m_ActToSeqHash.IsValidHandle(handle) ) - { + { // we already have an entry and must update it by incrementing count HashValueType * __restrict toUpdate = &m_ActToSeqHash.Element(handle); toUpdate->count += 1; @@ -1685,11 +1685,11 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict if (!bFoundOne) return; - // Now, create starting indices for each activity. For an activity n, - // the starting index is of course the sum of counts [0..n-1]. + // Now, create starting indices for each activity. For an activity n, + // the starting index is of course the sum of counts [0..n-1]. int sequenceCount = 0; int topActivity = 0; // this will store the highest seen activity number (used later to make an ad hoc map on the stack) - for ( UtlHashHandle_t handle = m_ActToSeqHash.GetFirstHandle() ; + for ( UtlHashHandle_t handle = m_ActToSeqHash.GetFirstHandle() ; m_ActToSeqHash.IsValidHandle(handle) ; handle = m_ActToSeqHash.GetNextHandle(handle) ) { @@ -1698,7 +1698,7 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict sequenceCount += element.count; topActivity = max(topActivity, element.activityIdx); } - + // Allocate the actual array of sequence information. Note the use of restrict; // this is an important optimization, but means that you must never refer to this @@ -1709,18 +1709,18 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict - // Now we're going to actually populate that list with the relevant data. + // Now we're going to actually populate that list with the relevant data. // First, create an array on the stack to store how many sequences we've written // so far for each activity. (This is basically a very simple way of doing a map.) - // This stack may potentially grow very large; so if you have problems with it, + // This stack may potentially grow very large; so if you have problems with it, // go to a utlmap or similar structure. unsigned int allocsize = (topActivity + 1) * sizeof(int); -#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression +//#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression allocsize = ALIGN_VALUE(allocsize,16); int * __restrict seqsPerAct = static_cast(stackalloc(allocsize)); memset(seqsPerAct, 0, allocsize); - // okay, walk through all the sequences again, and write the relevant data into + // okay, walk through all the sequences again, and write the relevant data into // our little table. for ( int i = 0 ; i < NumSeq ; ++i ) { @@ -1728,8 +1728,8 @@ void CStudioHdr::CActivityToSequenceMapping::Initialize( CStudioHdr * __restrict if (seqdesc.activity >= 0) { const HashValueType &element = m_ActToSeqHash[m_ActToSeqHash.Find(HashValueType(seqdesc.activity, 0, 0, 0))]; - - // If this assert trips, we've written more sequences per activity than we allocated + + // If this assert trips, we've written more sequences per activity than we allocated // (therefore there must have been a miscount in the first for loop above). int tupleOffset = seqsPerAct[seqdesc.activity]; Assert( tupleOffset < element.count ); @@ -1802,7 +1802,7 @@ const CStudioHdr::CActivityToSequenceMapping::SequenceTuple *CStudioHdr::CActivi HashValueType entry(forActivity, 0, 0, 0); UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); - + if (m_ActToSeqHash.IsValidHandle(handle)) { const HashValueType &element = m_ActToSeqHash[handle]; @@ -1822,7 +1822,7 @@ const CStudioHdr::CActivityToSequenceMapping::SequenceTuple *CStudioHdr::CActivi int CStudioHdr::CActivityToSequenceMapping::NumSequencesForActivity( int forActivity ) { - // If this trips, you've called this function on something that doesn't + // If this trips, you've called this function on something that doesn't // have activities. //Assert(m_pSequenceTuples != NULL); if ( m_pSequenceTuples == NULL ) diff --git a/public/studio.h b/public/studio.h index 5742d3f4c..d64986606 100644 --- a/public/studio.h +++ b/public/studio.h @@ -2240,7 +2240,7 @@ struct studiohdr_t int GetSequenceActivity( int iSequence ); void SetSequenceActivity( int iSequence, int iActivity ); int GetActivityListVersion( void ); - void SetActivityListVersion( int version ) const; + void SetActivityListVersion( int iVersion ) const; int GetEventListVersion( void ); void SetEventListVersion( int version ); @@ -2288,7 +2288,7 @@ struct studiohdr_t //public: int EntryNode( int iSequence ); int ExitNode( int iSequence ); - char *pszNodeName( int iNode ); + const char *pszNodeName( int iNode ); int GetTransition( int iFrom, int iTo ) const; int numflexdesc; @@ -2501,7 +2501,7 @@ class CStudioHdr int EntryNode( int iSequence ); int ExitNode( int iSequence ); - char *pszNodeName( int iNode ); + const char *pszNodeName( int iNode ); // FIXME: where should this one be? int GetTransition( int iFrom, int iTo ) const; diff --git a/public/tier0/dbg.h b/public/tier0/dbg.h index 24e64b019..63d934d1c 100644 --- a/public/tier0/dbg.h +++ b/public/tier0/dbg.h @@ -15,7 +15,9 @@ #include "basetypes.h" #include "dbgflag.h" #include "platform.h" +#if _MSC_VER < 1900 #include +#endif #include #include diff --git a/public/tier0/memalloc.h b/public/tier0/memalloc.h index 60fab0d1f..2ac27465a 100644 --- a/public/tier0/memalloc.h +++ b/public/tier0/memalloc.h @@ -385,7 +385,11 @@ class CMemAllocAttributeAlloction #pragma warning(disable:4290) #pragma warning(push) +#if _MSC_VER < 1900 + #include +#else #include +#endif // _MSC_VER // MEM_DEBUG_CLASSNAME is opt-in. // Note: typeid().name() is not threadsafe, so if the project needs to access it in multiple threads diff --git a/public/tier0/memdbgon.h b/public/tier0/memdbgon.h index b9eec5dd1..bed372663 100644 --- a/public/tier0/memdbgon.h +++ b/public/tier0/memdbgon.h @@ -37,7 +37,7 @@ #include "commonmacros.h" #include "memalloc.h" -#if defined(USE_MEM_DEBUG) +#if USE_MEM_DEBUG #if defined( POSIX ) #define _NORMAL_BLOCK 1 @@ -98,7 +98,7 @@ inline void *MemAlloc_InlineCallocMemset( void *pMem, size_t nCount, size_t nEle } #endif -#define calloc(c, s) MemAlloc_InlineCallocMemset(malloc(c*s), c, s) +#define calloc(c, s) MemAlloc_InlineCallocMemset(malloc((c)*(s)), (c), (s)) #define free(p) g_pMemAlloc->Free( p ) #define _msize(p) g_pMemAlloc->GetSize( p ) #define _expand(p, s) _expand_NoLongerSupported(p, s) @@ -106,7 +106,7 @@ inline void *MemAlloc_InlineCallocMemset( void *pMem, size_t nCount, size_t nEle // -------------------------------------------------------- // Debug path -#if defined(USE_MEM_DEBUG) +#if USE_MEM_DEBUG #define malloc(s) g_pMemAlloc->Alloc( s, __FILE__, __LINE__) #define realloc(p, s) g_pMemAlloc->Realloc( p, s, __FILE__, __LINE__ ) @@ -242,7 +242,7 @@ inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString) #else -#if defined(USE_MEM_DEBUG) +#if USE_MEM_DEBUG #ifndef _STATIC_LINKED #pragma message ("Note: file includes crtdbg.h directly, therefore will cannot use memdbgon.h in non-debug build") #else diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 73047e1bf..4c11ca3f2 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -433,7 +433,7 @@ typedef void * HINSTANCE; #define MAX_UNICODE_PATH MAX_PATH #endif -#define MAX_UNICODE_PATH_IN_UTF8 MAX_UNICODE_PATH*4 +#define MAX_UNICODE_PATH_IN_UTF8 (MAX_UNICODE_PATH*4) #if !defined( offsetof ) #ifdef __GNUC__ @@ -444,7 +444,7 @@ typedef void * HINSTANCE; #endif // !defined( offsetof ) -#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression +#define ALIGN_VALUE( val, alignment ) ( ( (val) + (alignment) - 1 ) & ~( (alignment) - 1 ) ) // need macro for constant expression // Used to step into the debugger #if defined( _WIN32 ) && !defined( _X360 ) @@ -687,7 +687,16 @@ typedef void * HINSTANCE; #endif // GCC 3.4.1 has a bug in supporting forced inline of templated functions // this macro lets us not force inlining in that case +#if __GNUC__ < 4 #define FORCEINLINE_TEMPLATE inline +#else + #define FORCEINLINE_TEMPLATE inline __attribute__((always_inline)) +#endif // __GNUC__ +#if __cpp_constexpr >= 201304 +#define CONSTEXPR_FUNC constexpr +#else +#define CONSTEXPR_FUNC +#endif // __cpp_constexpr // #define __stdcall __attribute__ ((__stdcall__)) #endif @@ -792,6 +801,40 @@ typedef void * HINSTANCE; #pragma warning( disable : 4312 ) // conversion from 'unsigned int' to 'memhandle_t' of greater size #endif +// Detect C++11 support for "rvalue references" / "move semantics" / other C++11 (and up) stuff +#if defined(_MSC_VER) +#if _MSC_VER >= 1600 +#define VALVE_RVALUE_REFS 1 +#endif +#if _MSC_VER >= 1800 +#define VALVE_INITIALIZER_LIST_SUPPORT 1 +#define VALVE_EXPLICIT_CONVERSION_OP 1 +#endif +#elif defined(__clang__) +#if __has_extension(cxx_rvalue_references) +#define VALVE_RVALUE_REFS 1 +#endif +#if __has_feature(cxx_generalized_initializers) +#define VALVE_INITIALIZER_LIST_SUPPORT 1 +#endif +#if __has_feature(cxx_explicit_conversions) +#define VALVE_EXPLICIT_CONVERSION_OP 1 +#endif +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 ) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#define VALVE_RVALUE_REFS 1 +#define VALVE_INITIALIZER_LIST_SUPPORT 1 +#define VALVE_EXPLICIT_CONVERSION_OP 1 +#endif +#endif +#endif + +#ifdef VALVE_RVALUE_REFS +#include "tier0/valve_minmax_off.h" +#include +#include "tier0/valve_minmax_on.h" +#endif #ifdef POSIX #define _stricmp stricmp @@ -824,29 +867,6 @@ typedef uintp HMODULE; typedef void *HANDLE; #endif -//----------------------------------------------------------------------------- -// fsel -//----------------------------------------------------------------------------- -#ifndef _X360 - -static FORCEINLINE float fsel(float fComparand, float fValGE, float fLT) -{ - return fComparand >= 0 ? fValGE : fLT; -} -static FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) -{ - return fComparand >= 0 ? fValGE : fLT; -} - -#else - -// __fsel(double fComparand, double fValGE, double fLT) == fComparand >= 0 ? fValGE : fLT -// this is much faster than if ( aFloat > 0 ) { x = .. } -#define fsel __fsel - -#endif - - //----------------------------------------------------------------------------- // FP exception handling //----------------------------------------------------------------------------- @@ -1422,6 +1442,13 @@ inline bool Plat_IsInDebugSession( bool bForceRecheck = false ) { return false; //----------------------------------------------------------------------------- PLATFORM_INTERFACE bool Is64BitOS(); +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// General Mapbase version constants compiled into projects for versioning purposes +//----------------------------------------------------------------------------- +#define MAPBASE_VERSION "7.1" +#define MAPBASE_VER_INT 7100 // For use in #if in a similar fashion to macros like _MSC_VER +#endif // MAPBASE //----------------------------------------------------------------------------- // XBOX Components valid in PC compilation space @@ -1494,63 +1521,82 @@ inline const char *GetPlatformExt( void ) template inline T* Construct( T* pMemory ) { + HINT(pMemory != 0); return reinterpret_cast(::new( pMemory ) T); } template inline T* Construct( T* pMemory, ARG1 a1 ) { + HINT(pMemory != 0); return reinterpret_cast(::new( pMemory ) T( a1 )); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2 ) { + HINT(pMemory != 0); return reinterpret_cast(::new( pMemory ) T( a1, a2 )); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3 ) { + HINT(pMemory != 0); return reinterpret_cast(::new( pMemory ) T( a1, a2, a3 )); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4 ) { + HINT(pMemory != 0); return reinterpret_cast(::new( pMemory ) T( a1, a2, a3, a4 )); } template inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4, ARG5 a5 ) { + HINT(pMemory != 0); return reinterpret_cast(::new( pMemory ) T( a1, a2, a3, a4, a5 )); } template inline void ConstructOneArg( T* pMemory, P const& arg) { + HINT(pMemory != 0); ::new( pMemory ) T(arg); } template inline void ConstructTwoArg( T* pMemory, P1 const& arg1, P2 const& arg2) { + HINT(pMemory != 0); ::new( pMemory ) T(arg1, arg2); } template inline void ConstructThreeArg( T* pMemory, P1 const& arg1, P2 const& arg2, P3 const& arg3) { + HINT(pMemory != 0); ::new( pMemory ) T(arg1, arg2, arg3); } template inline T* CopyConstruct( T* pMemory, T const& src ) { + HINT(pMemory != 0); return ::new( pMemory ) T(src); } +#ifdef VALVE_RVALUE_REFS +template +inline void CopyConstruct(T* pMemory, T&& src) +{ + HINT(pMemory != 0); + ::new(pMemory)T(std::forward(src)); +} +#endif // VALVE_RVALUE_REFS + template inline void Destruct( T* pMemory ) { diff --git a/public/tier1/UtlSortVector.h b/public/tier1/UtlSortVector.h index 9890c607c..02756f70b 100644 --- a/public/tier1/UtlSortVector.h +++ b/public/tier1/UtlSortVector.h @@ -244,7 +244,7 @@ void CUtlSortVector::QuickSort( LessFunc& less, int nLo ctx.m_pLessContext = m_pLessContext; ctx.m_pLessFunc = &less; - qsort_s( Base(), Count(), sizeof(T), (QSortCompareFunc_t)&CUtlSortVector::CompareHelper, &ctx ); + qsort_s( this->Base(), this->Count(), sizeof(T), (QSortCompareFunc_t)&CUtlSortVector::CompareHelper, &ctx ); } #else typedef int (__cdecl *QSortCompareFunc_t)( const void *, const void *); diff --git a/public/tier1/byteswap.h b/public/tier1/byteswap.h index 1c298f637..cb62f738e 100644 --- a/public/tier1/byteswap.h +++ b/public/tier1/byteswap.h @@ -186,7 +186,7 @@ class CByteswap if( !m_bSwapBytes || ( sizeof(T) == 1 ) ) { // If we were just going to swap in place then return. - if( !inputBuffer ) + if( inputBuffer == outputBuffer ) return; // Otherwise copy the inputBuffer to the outputBuffer: diff --git a/public/tier1/convar.h b/public/tier1/convar.h index f9f95355c..10d7f2565 100644 --- a/public/tier1/convar.h +++ b/public/tier1/convar.h @@ -21,6 +21,7 @@ #include "tier1/utlvector.h" #include "tier1/utlstring.h" #include "icvar.h" +#include "Color.h" #ifdef _WIN32 #define FORCEINLINE_CVAR FORCEINLINE @@ -300,6 +301,10 @@ friend class CCvar; ICommandCallback *m_pCommandCallback; }; +#ifdef MAPBASE_VSCRIPT + // Allow late modification of the completion callback. +public: +#endif // MAPBASE_VSCRIPT union { FnCommandCompletionCallback m_fnCompletionCallback; @@ -307,6 +312,9 @@ friend class CCvar; }; bool m_bHasCompletionCallback : 1; +#ifdef MAPBASE_VSCRIPT +private: +#endif // MAPBASE_VSCRIPT bool m_bUsingNewCommandCallback : 1; bool m_bUsingCommandCallbackInterface : 1; }; @@ -356,6 +364,7 @@ friend class ConVarRef; // Retrieve value FORCEINLINE_CVAR float GetFloat( void ) const; FORCEINLINE_CVAR int GetInt( void ) const; + FORCEINLINE_CVAR Color GetColor( void ) const; FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); } FORCEINLINE_CVAR char const *GetString( void ) const; @@ -461,6 +470,15 @@ FORCEINLINE_CVAR int ConVar::GetInt( void ) const return m_pParent->m_nValue; } +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVar::GetColor( void ) const +{ + unsigned char *pColorElement = ((unsigned char *)&m_pParent->m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} //----------------------------------------------------------------------------- // Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. @@ -507,8 +525,9 @@ class ConVarRef // Get/Set value float GetFloat( void ) const; - int GetInt( void ) const; - bool GetBool() const { return !!GetInt(); } + int GetInt( void ) const; + Color GetColor( void ) const; } + bool GetBool() const { return !!GetInt(); const char *GetString( void ) const; // True if it has a min/max setting bool GetMin( float& minVal ) const; @@ -565,6 +584,16 @@ FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const return m_pConVarState->m_nValue; } +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVarRef::GetColor( void ) const +{ + unsigned char *pColorElement = ((unsigned char *)&m_pConVarState->m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} + //----------------------------------------------------------------------------- // Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. //----------------------------------------------------------------------------- @@ -688,6 +717,18 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu static ConCommand name##_command( #name, name, description ); \ static void name( const CCommand &args ) +#ifdef CLIENT_DLL + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description ); \ + static void name( const CCommand &args ) +#endif // CLIENT_DLL + #define CON_COMMAND_F( name, description, flags ) \ static void name( const CCommand &args ); \ static ConCommand name##_command( #name, name, description, flags ); \ diff --git a/public/tier1/mapbase_con_groups.h b/public/tier1/mapbase_con_groups.h new file mode 100644 index 000000000..9282bd50c --- /dev/null +++ b/public/tier1/mapbase_con_groups.h @@ -0,0 +1,63 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: See tier1/mapbase_con_groups.cpp for more information +// +// $NoKeywords: $ +//============================================================================= + +#ifndef CON_VERBOSE_COLORS_H +#define CON_VERBOSE_COLORS_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//static const Color CON_COLOR_DEV_VERBOSE( 192, 128, 192, 255 ); + +enum ConGroupID_t +{ + // General + CON_GROUP_MAPBASE_MISC, // "Mapbase misc." + CON_GROUP_PHYSICS, // "Physics" + CON_GROUP_IO_SYSTEM, // "Entity I/O" + CON_GROUP_RESPONSE_SYSTEM, // "Response System" + + // Game + CON_GROUP_NPC_AI, // "NPC AI" + CON_GROUP_NPC_SCRIPTS, // "NPC scripts" + CON_GROUP_SPEECH_AI, // "Speech AI" + CON_GROUP_CHOREO, // "Choreo" + + // VScript + CON_GROUP_VSCRIPT, // "VScript" + CON_GROUP_VSCRIPT_PRINT, // "VScript print" + + //-------------------------- + + // + // Mod groups can be inserted here + // + + //-------------------------- + + CON_GROUP_MAX, // Keep this at the end +}; + +// Mapbase console group message. +void CGMsg( int level, ConGroupID_t nGroup, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 3, 4 ); + +#define CGWarning CGMsg + +//----------------------------------------------------------------------------- + +class IBaseFileSystem; + +void InitConsoleGroups( IBaseFileSystem *filesystem ); + +void PrintAllConsoleGroups(); +void ToggleConsoleGroups( const char *pszQuery ); +void SetConsoleGroupIncludeNames( bool bToggle ); + +#endif // CON_VERBOSE_COLORS_H diff --git a/public/tier1/mapbase_matchers_base.h b/public/tier1/mapbase_matchers_base.h new file mode 100644 index 000000000..4015203f1 --- /dev/null +++ b/public/tier1/mapbase_matchers_base.h @@ -0,0 +1,61 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: General matching functions for things like wildcards and !=. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef MAPBASE_MATCHERS_BASE_H +#define MAPBASE_MATCHERS_BASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +#define MAPBASE_MATCHERS 1 + +// Regular expressions based off of the std library. +// pszQuery = The regex text. +// szValue = The value that should be matched. +bool Matcher_Regex( const char *pszQuery, const char *szValue ); + +// Compares two strings with support for wildcards or regex. This code is an expanded version of baseentity.cpp's NamesMatch(). +// pszQuery = The value that should have the wildcard. +// szValue = The value tested against the query. +// Use Matcher_Match if you want <, !=, etc. as well. +bool Matcher_NamesMatch( const char *pszQuery, const char *szValue ); + +// Identical to baseentity.cpp's original NamesMatch(). +// pszQuery = The value that should have the wildcard. +// szValue = The value tested against the query. +bool Matcher_NamesMatch_Classic( const char *pszQuery, const char *szValue ); + +// Identical to Matcher_NamesMatch_Classic(), but either value could use a wildcard. +// pszQuery = The value that serves as the query. This value can use wildcards. +// szValue = The value tested against the query. This value can use wildcards as well. +bool Matcher_NamesMatch_MutualWildcard( const char *pszQuery, const char *szValue ); + +// Returns true if the specified string contains a wildcard character. +bool Matcher_ContainsWildcard( const char *pszQuery ); + +// Taken from the Response System. +// Checks if the specified string appears to be a number of some sort. +static bool AppearsToBeANumber( char const *token ) +{ + if ( atof( token ) != 0.0f ) + return true; + + char const *p = token; + while ( *p ) + { + if ( *p != '0' ) + return false; + + p++; + } + + return true; +} + +#endif // MAPBASE_MATCHERS_BASE_H diff --git a/public/tier1/utlblockmemory.h b/public/tier1/utlblockmemory.h index 35ef2da2e..73e6480ed 100644 --- a/public/tier1/utlblockmemory.h +++ b/public/tier1/utlblockmemory.h @@ -137,10 +137,10 @@ CUtlBlockMemory::~CUtlBlockMemory() template< class T, class I > void CUtlBlockMemory::Swap( CUtlBlockMemory< T, I > &mem ) { - this->swap( m_pMemory, mem.m_pMemory ); - this->swap( m_nBlocks, mem.m_nBlocks ); - this->swap( m_nIndexMask, mem.m_nIndexMask ); - this->swap( m_nIndexShift, mem.m_nIndexShift ); + this->V_swap( m_pMemory, mem.m_pMemory ); + this->V_swap( m_nBlocks, mem.m_nBlocks ); + this->V_swap( m_nIndexMask, mem.m_nIndexMask ); + this->V_swap( m_nIndexShift, mem.m_nIndexShift ); } diff --git a/public/tier1/utlbuffer.h b/public/tier1/utlbuffer.h index e71178f97..7f0ffeb46 100644 --- a/public/tier1/utlbuffer.h +++ b/public/tier1/utlbuffer.h @@ -670,7 +670,11 @@ inline void CUtlBuffer::GetObject( T *dest ) { if ( CheckGet( sizeof(T) ) ) { +#ifdef MAPBASE + if ( ( sizeof( T ) == 1 ) || !m_Byteswap.IsSwappingBytes() ) +#else if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#endif // MAPBASE { memcpy( dest, PeekGet(), sizeof( T ) ); } @@ -702,7 +706,11 @@ inline void CUtlBuffer::GetTypeBin( T &dest ) { if ( CheckGet( sizeof(T) ) ) { +#ifdef MAPBASE + if ( ( sizeof( T ) == 1 ) || !m_Byteswap.IsSwappingBytes() ) +#else if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#endif // MAPBASE { memcpy(&dest, PeekGet(), sizeof(T) ); } @@ -1076,7 +1084,11 @@ inline void CUtlBuffer::PutTypeBin( T src ) { if ( CheckPut( sizeof(T) ) ) { +#ifdef MAPBASE + if ( ( sizeof( T ) == 1 ) || !m_Byteswap.IsSwappingBytes() ) +#else if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) +#endif // MAPBASE { memcpy( PeekPut(), &src, sizeof( T ) ); } diff --git a/public/tier1/utlsoacontainer.h b/public/tier1/utlsoacontainer.h index e6605dc7d..f7292689f 100644 --- a/public/tier1/utlsoacontainer.h +++ b/public/tier1/utlsoacontainer.h @@ -109,6 +109,95 @@ template class CStridedConstPtr } }; +class CFltx4StridedPtr +{ +private: + typedef __m128 T; + +protected: + T *m_pData; + size_t m_nStride; + +public: + FORCEINLINE CFltx4StridedPtr( void *pData, size_t nByteStride ) + { + m_pData = reinterpret_cast( pData ); + m_nStride = nByteStride / sizeof( T ); + } + + FORCEINLINE CFltx4StridedPtr( void ) {} + T *operator->(void) const + { + return m_pData; + } + + T & operator*(void) const + { + return *m_pData; + } + + FORCEINLINE operator T *(void) + { + return m_pData; + } + + FORCEINLINE CFltx4StridedPtr& operator++(void) + { + m_pData += m_nStride; + return *this; + } + + FORCEINLINE void operator+=( size_t nNumElements ) + { + m_pData += nNumElements * m_nStride; + } + +}; + +class CFltx4StridedConstPtr +{ +private: + typedef __m128 T; + +protected: + const T *m_pData; + size_t m_nStride; + +public: + FORCEINLINE CFltx4StridedConstPtr( void const *pData, size_t nByteStride ) + { + m_pData = reinterpret_cast( pData ); + m_nStride = nByteStride / sizeof( T ); + } + + FORCEINLINE CFltx4StridedConstPtr( void ) {} + + const T *operator->(void) const + { + return m_pData; + } + + const T & operator*(void) const + { + return *m_pData; + } + + FORCEINLINE operator const T *(void) const + { + return m_pData; + } + + FORCEINLINE CFltx4StridedConstPtr &operator++(void) + { + m_pData += m_nStride; + return *this; + } + FORCEINLINE void operator+=( size_t nNumElements ) + { + m_pData += nNumElements*m_nStride; + } +}; + // allowed field data types. if you change these values, you need to change the tables in the .cpp file enum EAttributeDataType { @@ -311,19 +400,19 @@ class CSOAContainer }; -class CFltX4AttributeIterator : public CStridedConstPtr +class CFltX4AttributeIterator : public CFltx4StridedConstPtr { FORCEINLINE CFltX4AttributeIterator( CSOAContainer const *pContainer, int nAttribute, int nRowNumber = 0 ) - : CStridedConstPtr( pContainer->ConstRowPtr( nAttribute, nRowNumber), + : CFltx4StridedConstPtr(pContainer->ConstRowPtr( nAttribute, nRowNumber), pContainer->ItemByteStride( nAttribute ) ) { } }; -class CFltX4AttributeWriteIterator : public CStridedPtr +class CFltX4AttributeWriteIterator : public CFltx4StridedConstPtr { FORCEINLINE CFltX4AttributeWriteIterator( CSOAContainer const *pContainer, int nAttribute, int nRowNumber = 0 ) - : CStridedPtr( pContainer->RowPtr( nAttribute, nRowNumber), + : CFltx4StridedConstPtr( pContainer->RowPtr( nAttribute, nRowNumber), pContainer->ItemByteStride( nAttribute ) ) { } diff --git a/public/vscript/ivscript.h b/public/vscript/ivscript.h new file mode 100644 index 000000000..1b9622c8e --- /dev/null +++ b/public/vscript/ivscript.h @@ -0,0 +1,2101 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: VScript +// +// Overview +// -------- +// VScript is an abstract binding layer that allows code to expose itself to +// multiple scripting languages in a uniform format. Code can expose +// functions, classes, and data to the scripting languages, and can also +// call functions that reside in scripts. +// +// Initializing +// ------------ +// +// To create a script virtual machine (VM), grab the global instance of +// IScriptManager, call CreateVM, then call Init on the returned VM. Right +// now you can have multiple VMs, but only VMs for a specific language. +// +// Exposing functions and classes +// ------------------------------ +// +// To expose a C++ function to the scripting system, you just need to fill out a +// description block. Using templates, the system will automatically deduce +// all of the binding requirements (parameters and return values). Functions +// are limited as to what the types of the parameters can be. See ScriptVariant_t. +// +// extern IScriptVM *pScriptVM; +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// +// void RegisterFuncs() +// { +// ScriptRegisterFunction( pScriptVM, Foo ); +// ScriptRegisterFunction( pScriptVM, Bar ); +// ScriptRegisterFunction( pScriptVM, FooBar ); +// ScriptRegisterFunctionNamed( pScriptVM, OverlyTechnicalName, "SimpleName" ); +// } +// +// class CMyClass +// { +// public: +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// }; +// +// BEGIN_SCRIPTDESC_ROOT( CMyClass ) +// DEFINE_SCRIPTFUNC( Foo ) +// DEFINE_SCRIPTFUNC( Bar ) +// DEFINE_SCRIPTFUNC( FooBar ) +// DEFINE_SCRIPTFUNC_NAMED( OverlyTechnicalName, "SimpleMemberName" ) +// END_SCRIPTDESC(); +// +// class CMyDerivedClass : public CMyClass +// { +// public: +// float DerivedFunc() const; +// }; +// +// BEGIN_SCRIPTDESC( CMyDerivedClass, CMyClass ) +// DEFINE_SCRIPTFUNC( DerivedFunc ) +// END_SCRIPTDESC(); +// +// CMyDerivedClass derivedInstance; +// +// void AnotherFunction() +// { +// // Manual class exposure +// pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) ); +// +// // Auto registration by instance +// pScriptVM->RegisterInstance( &derivedInstance, "theInstance" ); +// } +// +// Classes with "DEFINE_SCRIPT_CONSTRUCTOR()" in their description can be instanced within scripts +// +// Scopes +// ------ +// Scripts can either be run at the global scope, or in a user defined scope. In the latter case, +// all "globals" within the script are actually in the scope. This can be used to bind private +// data spaces with C++ objects. +// +// Calling a function on a script +// ------------------------------ +// Generally, use the "Call" functions. This example is the equivalent of DoIt("Har", 6.0, 99). +// +// hFunction = pScriptVM->LookupFunction( "DoIt", hScope ); +// pScriptVM->Call( hFunction, hScope, true, NULL, "Har", 6.0, 99 ); +// +//============================================================================= + +#ifndef IVSCRIPT_H +#define IVSCRIPT_H + +#include +#include + +#include "utlmap.h" +#include "utlvector.h" + +#include "platform.h" +#include "datamap.h" +#include "appframework/IAppSystem.h" +#include "tier1/functors.h" +#include "tier0/memdbgon.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifdef VSCRIPT_DLL_EXPORT +#define VSCRIPT_INTERFACE DLL_EXPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_EXPORT +#define VSCRIPT_CLASS DLL_CLASS_EXPORT +#else +#define VSCRIPT_INTERFACE DLL_IMPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_IMPORT +#define VSCRIPT_CLASS DLL_CLASS_IMPORT +#endif + +class CUtlBuffer; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define VSCRIPT_INTERFACE_VERSION "VScriptManager009" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class IScriptVM; +#ifdef MAPBASE_VSCRIPT +class KeyValues; + +// This has been moved up a bit for IScriptManager +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) + +typedef unsigned int HScriptRaw; +#endif + +enum ScriptLanguage_t +{ + SL_NONE, + SL_GAMEMONKEY, + SL_SQUIRREL, + SL_LUA, + SL_PYTHON, + + SL_DEFAULT = SL_SQUIRREL +}; + +class IScriptManager : public IAppSystem +{ +public: + virtual IScriptVM *CreateVM( ScriptLanguage_t language = SL_DEFAULT ) = 0; + virtual void DestroyVM( IScriptVM * ) = 0; + +#ifdef MAPBASE_VSCRIPT + virtual HSCRIPT CreateScriptKeyValues( IScriptVM *pVM, KeyValues *pKV, bool bAllowDestruct ) = 0; + virtual KeyValues *GetKeyValuesFromScriptKV( IScriptVM *pVM, HSCRIPT hSKV ) = 0; +#endif +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ExtendedFieldType +{ + FIELD_TYPEUNKNOWN = FIELD_TYPECOUNT, + FIELD_CSTRING, + FIELD_HSCRIPT, + FIELD_VARIANT, +}; + +typedef int ScriptDataType_t; +struct ScriptVariant_t; + +template struct ScriptDeducer { /*enum { FIELD_TYPE = FIELD_TYPEUNKNOWN };*/ }; +#define DECLARE_DEDUCE_FIELDTYPE( fieldType, type ) template<> struct ScriptDeducer { enum { FIELD_TYPE = fieldType }; }; + +DECLARE_DEDUCE_FIELDTYPE( FIELD_VOID, void ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_FLOAT, float ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, const char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, Vector ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const Vector &); +DECLARE_DEDUCE_FIELDTYPE( FIELD_INTEGER, int ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_BOOLEAN, bool ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CHARACTER, char ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_HSCRIPT, HSCRIPT ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VARIANT, ScriptVariant_t ); +#ifdef MAPBASE_VSCRIPT +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, QAngle ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const QAngle& ); +#endif + +#define ScriptDeduceType( T ) ScriptDeducer::FIELD_TYPE + +template +inline const char * ScriptFieldTypeName() +{ + return T::using_unknown_script_type(); +} + +#define DECLARE_NAMED_FIELDTYPE( fieldType, strName ) template <> inline const char * ScriptFieldTypeName() { return strName; } +DECLARE_NAMED_FIELDTYPE( void, "void" ); +DECLARE_NAMED_FIELDTYPE( float, "float" ); +DECLARE_NAMED_FIELDTYPE( const char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( Vector, "vector" ); +DECLARE_NAMED_FIELDTYPE( const Vector&, "vector" ); +DECLARE_NAMED_FIELDTYPE( int, "integer" ); +DECLARE_NAMED_FIELDTYPE( bool, "boolean" ); +DECLARE_NAMED_FIELDTYPE( char, "character" ); +DECLARE_NAMED_FIELDTYPE( HSCRIPT, "hscript" ); +DECLARE_NAMED_FIELDTYPE( ScriptVariant_t, "variant" ); +#ifdef MAPBASE_VSCRIPT +DECLARE_NAMED_FIELDTYPE( QAngle, "vector" ); +DECLARE_NAMED_FIELDTYPE( const QAngle&, "vector" ); +#endif + +inline const char * ScriptFieldTypeName( int16 eType) +{ + switch( eType ) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "cstring"; + case FIELD_VECTOR: return "vector"; + case FIELD_INTEGER: return "integer"; + case FIELD_BOOLEAN: return "boolean"; + case FIELD_CHARACTER: return "character"; + case FIELD_HSCRIPT: return "hscript"; + case FIELD_VARIANT: return "variant"; + default: return "unknown_script_type"; + } +} + +//--------------------------------------------------------- + +struct ScriptFuncDescriptor_t +{ + ScriptFuncDescriptor_t() + { + m_pszFunction = NULL; + m_ReturnType = FIELD_TYPEUNKNOWN; + m_pszDescription = NULL; + } + + const char *m_pszScriptName; + const char *m_pszFunction; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; + CUtlVector m_Parameters; +}; + +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +// VScript Member Variables +// +// An odd concept. Because IScriptInstanceHelper now supports +// get/set metamethods, classes are capable of pretending they +// have member variables which VScript can get and set. +// +// There's no default way of documenting these variables, so even though +// these are not actually binding anything, this is here to allow VScript +// to describe these fake member variables in its documentation. +//--------------------------------------------------------- +struct ScriptMemberDesc_t +{ + const char *m_pszScriptName; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; +}; +#endif + + +//--------------------------------------------------------- + +// Prefix a script description with this in order to not show the function or class in help +#define SCRIPT_HIDE "@" + +// Prefix a script description of a class to indicate it is a singleton and the single instance should be in the help +#define SCRIPT_SINGLETON "!" + +// Prefix a script description with this to indicate it should be represented using an alternate name +#define SCRIPT_ALIAS( alias, description ) "#" alias ":" description + +//--------------------------------------------------------- + +enum ScriptFuncBindingFlags_t +{ + SF_MEMBER_FUNC = 0x01, +}; + +typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ); + +struct ScriptFunctionBinding_t +{ + ScriptFuncDescriptor_t m_desc; + ScriptBindingFunc_t m_pfnBinding; + void * m_pFunction; + unsigned m_flags; +}; + +//--------------------------------------------------------- +class IScriptInstanceHelper +{ +public: + virtual void *GetProxied( void *p ) { return p; } + virtual bool ToString( void *p, char *pBuf, int bufSize ) { return false; } + virtual void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) { return NULL; } + +#ifdef MAPBASE_VSCRIPT + virtual bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ) { return false; } + virtual bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ) { return false; } + + virtual ScriptVariant_t *Add( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Subtract( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Multiply( void *p, ScriptVariant_t &variant ) { return NULL; } + virtual ScriptVariant_t *Divide( void *p, ScriptVariant_t &variant ) { return NULL; } +#endif +}; + +//--------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT +struct ScriptHook_t; +#endif + +struct ScriptClassDesc_t +{ + ScriptClassDesc_t() : m_pszScriptName( 0 ), m_pszClassname( 0 ), m_pszDescription( 0 ), m_pBaseDesc( 0 ), m_pfnConstruct( 0 ), m_pfnDestruct( 0 ), pHelper(NULL) + { +#ifdef MAPBASE_VSCRIPT + AllClassesDesc().AddToTail(this); +#endif + } + + const char * m_pszScriptName; + const char * m_pszClassname; + const char * m_pszDescription; + ScriptClassDesc_t * m_pBaseDesc; + CUtlVector m_FunctionBindings; + +#ifdef MAPBASE_VSCRIPT + CUtlVector m_Hooks; + CUtlVector m_Members; +#endif + + void *(*m_pfnConstruct)(); + void (*m_pfnDestruct)( void *); + IScriptInstanceHelper * pHelper; // optional helper + +#ifdef MAPBASE_VSCRIPT + static CUtlVector& AllClassesDesc() + { + static CUtlVector classes; + return classes; + } +#endif +}; + +//--------------------------------------------------------- +// A simple variant type. Intentionally not full featured (no implicit conversion, no memory management) +//--------------------------------------------------------- + +enum SVFlags_t +{ + SV_FREE = 0x01, +}; + +#pragma warning(push) +#pragma warning(disable:4800) +struct ScriptVariant_t +{ + ScriptVariant_t() : m_flags( 0 ), m_type( FIELD_VOID ) { m_pVector = 0; } + ScriptVariant_t( int val ) : m_flags( 0 ), m_type( FIELD_INTEGER ) { m_int = val;} + ScriptVariant_t( float val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = val; } + ScriptVariant_t( double val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = (float)val; } + ScriptVariant_t( char val ) : m_flags( 0 ), m_type( FIELD_CHARACTER ) { m_char = val; } + ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; } + ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; } + + ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = &val; } else { m_pVector = new Vector( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const Vector *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = val; } else { m_pVector = new Vector( *val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { if ( !bCopy ) { m_pszString = val; } else { m_pszString = strdup( val ); m_flags |= SV_FREE; } } + +#ifdef MAPBASE_VSCRIPT + ScriptVariant_t( const QAngle &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pAngle = &val; } else { m_pAngle = new QAngle( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const QAngle *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pAngle = val; } else { m_pAngle = new QAngle( *val ); m_flags |= SV_FREE; } } +#endif + + bool IsNull() const { return (m_type == FIELD_VOID ); } + + operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; } + operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; } + operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; } + operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; } + operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; } + operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; } + operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; } +#ifdef MAPBASE_VSCRIPT + operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); static QAngle vecNull(0, 0, 0); return (m_pAngle) ? *m_pAngle : vecNull; } +#endif + + void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; } + void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; } + void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; } + void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; } + void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; } + void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; } + void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; } + void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; } + void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; } +#ifdef MAPBASE_VSCRIPT + void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_pAngle = &vec; } + void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_pAngle = vec; } +#endif + + void Free() { if ( ( m_flags & SV_FREE ) && ( m_type == FIELD_HSCRIPT || m_type == FIELD_VECTOR || m_type == FIELD_CSTRING ) ) delete m_pszString; } // Generally only needed for return results + + template + T Get() + { + T value; + AssignTo( &value ); + return value; + } + + template + bool AssignTo( T *pDest ) + { + ScriptDataType_t destType = ScriptDeduceType( T ); + if ( destType == FIELD_TYPEUNKNOWN ) + { + DevWarning( "Unable to convert script variant to unknown type\n" ); + } + if ( destType == m_type ) + { + *pDest = *this; + return true; + } + + if ( m_type != FIELD_VECTOR && m_type != FIELD_CSTRING && destType != FIELD_VECTOR && destType != FIELD_CSTRING ) + { + switch ( m_type ) + { + case FIELD_VOID: *pDest = 0; break; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_CHARACTER: *pDest = m_char; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + case FIELD_HSCRIPT: *pDest = m_hScript; return true; + } + } + else + { + DevWarning( "No free conversion of %s script variant to %s right now\n", + ScriptFieldTypeName( m_type ), ScriptFieldTypeName() ); + if ( destType != FIELD_VECTOR ) + { + *pDest = 0; + } + } + return false; + } + + bool AssignTo( float *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to float now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( int *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to int now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( bool *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to bool now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( char **pDest ) + { + DevWarning( "No free conversion of string or vector script variant right now\n" ); + // If want to support this, probably need to malloc string and require free on other side [3/24/2008 tom] + *pDest = ""; + return false; + } + + bool AssignTo( ScriptVariant_t *pDest ) + { + pDest->m_type = m_type; + if ( m_type == FIELD_VECTOR ) + { + pDest->m_pVector = new Vector; + ((Vector *)(pDest->m_pVector))->Init( m_pVector->x, m_pVector->y, m_pVector->z ); + pDest->m_flags |= SV_FREE; + } + else if ( m_type == FIELD_CSTRING ) + { + pDest->m_pszString = strdup( m_pszString ); + pDest->m_flags |= SV_FREE; + } + else + { + pDest->m_int = m_int; + } + return false; + } + + union + { + int m_int; + float m_float; + const char * m_pszString; + const Vector * m_pVector; + char m_char; + bool m_bool; + HSCRIPT m_hScript; +#ifdef MAPBASE_VSCRIPT + // This uses FIELD_VECTOR, so it's considered a Vector in the VM (just like save/restore) + const QAngle * m_pAngle; +#endif + }; + + int16 m_type; + int16 m_flags; + +private: +}; + +#define SCRIPT_VARIANT_NULL ScriptVariant_t() + +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +struct ScriptConstantBinding_t +{ + const char *m_pszScriptName; + const char *m_pszDescription; + ScriptVariant_t m_data; + unsigned m_flags; +}; + +//--------------------------------------------------------- +struct ScriptEnumDesc_t +{ + ScriptEnumDesc_t() : m_pszScriptName( 0 ), m_pszDescription( 0 ), m_flags( 0 ) + { + AllEnumsDesc().AddToTail(this); + } + + virtual void RegisterDesc() = 0; + + const char *m_pszScriptName; + const char *m_pszDescription; + CUtlVector m_ConstantBindings; + unsigned m_flags; + + static CUtlVector& AllEnumsDesc() + { + static CUtlVector enums; + return enums; + } +}; +#endif + +#pragma warning(pop) + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#include "vscript_templates.h" + +// Lower level macro primitives +#define ScriptInitFunctionBinding( pScriptFunction, func ) ScriptInitFunctionBindingNamed( pScriptFunction, func, #func ) +#define ScriptInitFunctionBindingNamed( pScriptFunction, func, scriptName ) do { ScriptInitFuncDescriptorNamed( (&(pScriptFunction)->m_desc), func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( &func ); (pScriptFunction)->m_pFunction = (void *)&func; } while (0) + +#define ScriptInitMemberFunctionBinding( pScriptFunction, class, func ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, #func ) +#define ScriptInitMemberFunctionBindingNamed( pScriptFunction, class, func, scriptName ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) +#define ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) do { ScriptInitMemberFuncDescriptor_( (&(pScriptFunction)->m_desc), class, func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( ((class *)0), &class::func ); (pScriptFunction)->m_pFunction = ScriptConvertFuncPtrToVoid( &class::func ); (pScriptFunction)->m_flags = SF_MEMBER_FUNC; } while (0) + +#define ScriptInitClassDesc( pClassDesc, class, pBaseClassDesc ) ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, #class ) +#define ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) +#define ScriptInitClassDescNoBase( pClassDesc, class ) ScriptInitClassDescNoBaseNamed( pClassDesc, class, #class ) +#define ScriptInitClassDescNoBaseNamed( pClassDesc, class, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, NULL, scriptName ) +#define ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) do { (pClassDesc)->m_pszScriptName = scriptName; (pClassDesc)->m_pszClassname = #class; (pClassDesc)->m_pBaseDesc = pBaseClassDesc; } while ( 0 ) + +#define ScriptAddFunctionToClassDesc( pClassDesc, class, func, description ) ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, #func, description ) +#define ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, scriptName, description ) do { ScriptFunctionBinding_t *pBinding = &((pClassDesc)->m_FunctionBindings[(pClassDesc)->m_FunctionBindings.AddToTail()]); pBinding->m_desc.m_pszDescription = description; ScriptInitMemberFunctionBindingNamed( pBinding, class, func, scriptName ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterFunction( pVM, func, description ) ScriptRegisterFunctionNamed( pVM, func, #func, description ) +#define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) do { static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); pVM->RegisterFunction( &binding ); } while (0) + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +// forwards T (and T&) if T is neither enum or an unsigned integer +// the overload for int below captures enums and unsigned integers and "bends" them to int +template +static inline typename std::enable_if::type>::value && !std::is_unsigned::type>::value, T&&>::type ToConstantVariant(T &&value) +{ + return std::forward(value); +} + +static inline int ToConstantVariant(int value) +{ + return value; +} + +#define ScriptRegisterConstant( pVM, constant, description ) ScriptRegisterConstantNamed( pVM, constant, #constant, description ) +#define ScriptRegisterConstantNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ToConstantVariant(constant); pVM->RegisterConstant( &binding ); } while (0) + +// Could probably use a better name. +// This is used for registering variants (particularly vectors) not tied to existing variables. +// The principal difference is that m_data is initted with bCopy set to true. +#define ScriptRegisterConstantFromTemp( pVM, constant, description ) ScriptRegisterConstantFromTempNamed( pVM, constant, #constant, description ) +#define ScriptRegisterConstantFromTempNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ScriptVariant_t( constant, true ); pVM->RegisterConstant( &binding ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define BEGIN_SCRIPTENUM( enumName, description ) \ + struct ScriptEnum##enumName##Desc_t : public ScriptEnumDesc_t \ + { \ + void RegisterDesc(); \ + }; \ + ScriptEnum##enumName##Desc_t g_##enumName##_EnumDesc; \ + \ + void ScriptEnum##enumName##Desc_t::RegisterDesc() \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + return; \ + \ + bInitialized = true; \ + \ + m_pszScriptName = #enumName; \ + m_pszDescription = description; \ + +#define DEFINE_ENUMCONST( constant, description ) DEFINE_ENUMCONST_NAMED( constant, #constant, description ) +#define DEFINE_ENUMCONST_NAMED( constant, scriptName, description ) do { ScriptConstantBinding_t *pBinding = &(m_ConstantBindings[m_ConstantBindings.AddToTail()]); pBinding->m_pszScriptName = scriptName; pBinding->m_pszDescription = description; pBinding->m_data = constant; pBinding->m_flags = SF_MEMBER_FUNC; } while (0); + +#define END_SCRIPTENUM() \ + } \ + + +#define GetScriptDescForEnum( enumName ) GetScriptDesc( ( className *)NULL ) +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *); + +#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_NAMED( className, baseClass, #className, description ) +#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_NAMED( className, #className, description ) + +#define BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) \ + template <> ScriptClassDesc_t* GetScriptDesc(baseClass*); \ + template <> ScriptClassDesc_t* GetScriptDesc(className*); \ + ScriptClassDesc_t & g_##className##_ScriptDesc = *GetScriptDesc(nullptr); \ + template <> ScriptClassDesc_t* GetScriptDesc(className*) \ + { \ + static ScriptClassDesc_t g_##className##_ScriptDesc; \ + typedef className _className; \ + ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ + if (pDesc->m_pszClassname) return pDesc; \ + pDesc->m_pszDescription = description; \ + ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ + ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ + while ( pInstanceHelperBase ) \ + { \ + if ( pInstanceHelperBase->pHelper ) \ + { \ + pDesc->pHelper = pInstanceHelperBase->pHelper; \ + break; \ + } \ + pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \ + } + + +#define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \ + BEGIN_SCRIPTDESC_NAMED( className, ScriptNoBase_t, scriptName, description ) + +#define END_SCRIPTDESC() \ + return pDesc; \ + } + +#define DEFINE_SCRIPTFUNC( func, description ) DEFINE_SCRIPTFUNC_NAMED( func, #func, description ) +#define DEFINE_SCRIPTFUNC_NAMED( func, scriptName, description ) ScriptAddFunctionToClassDescNamed( pDesc, _className, func, scriptName, description ); +#define DEFINE_SCRIPT_CONSTRUCTOR() ScriptAddConstructorToClassDesc( pDesc, _className ); +#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) pDesc->pHelper = (p); + +#ifdef MAPBASE_VSCRIPT +// Use this for hooks which have no parameters +#define DEFINE_SIMPLE_SCRIPTHOOK( hook, hookName, returnType, description ) \ + if (!hook.m_bDefined) \ + { \ + ScriptHook_t *pHook = &hook; \ + pHook->m_desc.m_pszScriptName = hookName; pHook->m_desc.m_pszFunction = #hook; pHook->m_desc.m_ReturnType = returnType; pHook->m_desc.m_pszDescription = description; \ + pDesc->m_Hooks.AddToTail(pHook); \ + } + +#define BEGIN_SCRIPTHOOK( hook, hookName, returnType, description ) \ + if (!hook.m_bDefined) \ + { \ + ScriptHook_t *pHook = &hook; \ + pHook->m_desc.m_pszScriptName = hookName; pHook->m_desc.m_pszFunction = #hook; pHook->m_desc.m_ReturnType = returnType; pHook->m_desc.m_pszDescription = description; + +#define DEFINE_SCRIPTHOOK_PARAM( paramName, type ) pHook->AddParameter( paramName, type ); + +#define END_SCRIPTHOOK() \ + pDesc->m_Hooks.AddToTail(pHook); \ + } + +// Static hooks (or "global" hooks) are not tied to specific classes +#define END_SCRIPTHOOK_STATIC( pVM ) \ + pVM->RegisterHook( pHook ); \ + } + +#define ScriptRegisterSimpleHook( pVM, hook, hookName, returnType, description ) \ + if (!hook.m_bDefined) \ + { \ + ScriptHook_t *pHook = &hook; \ + pHook->m_desc.m_pszScriptName = hookName; pHook->m_desc.m_pszFunction = #hook; pHook->m_desc.m_ReturnType = returnType; pHook->m_desc.m_pszDescription = description; \ + pVM->RegisterHook( pHook ); \ + } + +#define ScriptRegisterConstant( pVM, constant, description ) ScriptRegisterConstantNamed( pVM, constant, #constant, description ) +#define ScriptRegisterConstantNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ToConstantVariant(constant); pVM->RegisterConstant( &binding ); } while (0) + + +#define DEFINE_MEMBERVAR( varName, returnType, description ) \ + do { ScriptMemberDesc_t *pBinding = &((pDesc)->m_Members[(pDesc)->m_Members.AddToTail()]); pBinding->m_pszScriptName = varName; pBinding->m_pszDescription = description; pBinding->m_ReturnType = returnType; } while (0); +#endif + +template ScriptClassDesc_t *GetScriptDesc(T *); + +struct ScriptNoBase_t; +template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *) { return NULL; } + +#define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +class CScriptConstructor +{ +public: + static void *Construct() { return new T; } + static void Destruct( void *p ) { delete (T *)p; } +}; + +#define ScriptAddConstructorToClassDesc( pClassDesc, class ) do { (pClassDesc)->m_pfnConstruct = &CScriptConstructor::Construct; (pClassDesc)->m_pfnDestruct = &CScriptConstructor::Destruct; } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ScriptErrorLevel_t +{ + SCRIPT_LEVEL_WARNING = 0, + SCRIPT_LEVEL_ERROR, +}; + +typedef void ( *ScriptOutputFunc_t )( const char *pszText ); +typedef bool ( *ScriptErrorFunc_t )( ScriptErrorLevel_t eLevel, const char *pszText ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef RegisterClass +#undef RegisterClass +#endif + +enum ScriptStatus_t +{ + SCRIPT_ERROR = -1, + SCRIPT_DONE, + SCRIPT_RUNNING, +}; + +class IScriptVM +{ +public: + virtual ~IScriptVM() {} + + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual bool ConnectDebugger() = 0; + virtual void DisconnectDebugger() = 0; + + virtual ScriptLanguage_t GetLanguage() = 0; + virtual const char *GetLanguageName() = 0; + + virtual void AddSearchPath( const char *pszSearchPath ) = 0; + + //-------------------------------------------------------- + + virtual bool Frame( float simTime ) = 0; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run( const char *pszScript, bool bWait = true ) = 0; + inline ScriptStatus_t Run( const unsigned char *pszScript, bool bWait = true ) { return Run( (char *)pszScript, bWait ); } + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) = 0; + inline HSCRIPT CompileScript( const unsigned char *pszScript, const char *pszId = NULL ) { return CompileScript( (char *)pszScript, pszId ); } + virtual void ReleaseScript( HSCRIPT ) = 0; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) = 0; + virtual ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) = 0; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) = 0; + virtual void ReleaseScope( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) = 0; + virtual void ReleaseFunction( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; + +#ifdef MAPBASE_VSCRIPT + //-------------------------------------------------------- + // Hooks + //-------------------------------------------------------- + // Persistent unique identifier for an HSCRIPT variable + virtual HScriptRaw HScriptToRaw( HSCRIPT val ) = 0; + virtual ScriptStatus_t ExecuteHookFunction( const char *pszEventName, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; +#endif + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) = 0; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) = 0; + +#ifdef MAPBASE_VSCRIPT + //-------------------------------------------------------- + // External constants + //-------------------------------------------------------- + virtual void RegisterConstant( ScriptConstantBinding_t *pScriptConstant ) = 0; + + //-------------------------------------------------------- + // External enums + //-------------------------------------------------------- + virtual void RegisterEnum( ScriptEnumDesc_t *pEnumDesc ) = 0; + + //-------------------------------------------------------- + // External hooks + //-------------------------------------------------------- + virtual void RegisterHook( ScriptHook_t *pHookDesc ) = 0; +#endif + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT + // When a RegisterInstance instance is deleted, VScript normally treats it as a strong reference and only deregisters the instance itself, preserving the registered data + // it points to so the game can continue to use it. + // bAllowDestruct is supposed to allow VScript to treat it as a weak reference created by the script, destructing the registered data automatically like any other type. + // This is useful for classes pretending to be primitive types. + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance, bool bAllowDestruct = false ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance, bool bAllowDestruct = false ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance, bAllowDestruct ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL, bool bAllowDestruct = false) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance, bAllowDestruct ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } +#else + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } +#endif + virtual void RemoveInstance( HSCRIPT ) = 0; + void RemoveInstance( HSCRIPT hInstance, const char *pszInstance, HSCRIPT hScope = NULL ) { ClearValue( hScope, pszInstance ); RemoveInstance( hInstance ); } + void RemoveInstance( const char *pszInstance, HSCRIPT hScope = NULL ) { ScriptVariant_t val; if ( GetValue( hScope, pszInstance, &val ) ) { if ( val.m_type == FIELD_HSCRIPT ) { RemoveInstance( val, pszInstance, hScope ); } ReleaseValue( val ); } } + + virtual void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType = NULL ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) = 0; + bool ValueExists( const char *pszKey ) { return ValueExists( NULL, pszKey ); } + + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) = 0; + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) = 0; + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return SetValue(NULL, pszKey, value ); } +#ifdef MAPBASE_VSCRIPT + virtual bool SetValue( HSCRIPT hScope, const ScriptVariant_t& key, const ScriptVariant_t& val ) = 0; +#endif + + virtual void CreateTable( ScriptVariant_t &Table ) = 0; + virtual int GetNumTableEntries( HSCRIPT hScope ) = 0; + virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) = 0; + + virtual bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) = 0; + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetValue(NULL, pszKey, pValue ); } +#ifdef MAPBASE_VSCRIPT + virtual bool GetValue( HSCRIPT hScope, ScriptVariant_t key, ScriptVariant_t* pValue ) = 0; +#endif + virtual void ReleaseValue( ScriptVariant_t &value ) = 0; + + virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) = 0; + bool ClearValue( const char *pszKey) { return ClearValue( NULL, pszKey ); } +#ifdef MAPBASE_VSCRIPT + virtual bool ClearValue( HSCRIPT hScope, ScriptVariant_t pKey ) = 0; +#endif + +#ifdef MAPBASE_VSCRIPT + virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; + virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) = 0; +#endif + + //---------------------------------------------------------------------------- + + virtual void WriteState( CUtlBuffer *pBuffer ) = 0; + virtual void ReadState( CUtlBuffer *pBuffer ) = 0; + virtual void RemoveOrphanInstances() = 0; + + virtual void DumpState() = 0; + + virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) = 0; + virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException( const char *pszExceptionText ) = 0; + + //---------------------------------------------------------------------------- + // Call API + // + // Note for string and vector return types, the caller must delete the pointed to memory + //---------------------------------------------------------------------------- + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope = NULL, bool bWait = true, ScriptVariant_t *pReturn = NULL ) + { + return ExecuteFunction( hFunction, NULL, 0, pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + +#ifdef MAPBASE_VSCRIPT + void RegisterAllClasses() + { + CUtlVector& classDescs = ScriptClassDesc_t::AllClassesDesc(); + FOR_EACH_VEC(classDescs, i) + { + RegisterClass(classDescs[i]); + } + } + + void RegisterAllEnums() + { + CUtlVector& enumDescs = ScriptEnumDesc_t::AllEnumsDesc(); + FOR_EACH_VEC(enumDescs, i) + { + enumDescs[i]->RegisterDesc(); + RegisterEnum(enumDescs[i]); + } + } +#endif +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef MAPBASE_VSCRIPT +template T *HScriptToClass( HSCRIPT hObj ) +{ + extern IScriptVM *g_pScriptVM; + return (hObj) ? (T*)g_pScriptVM->GetInstanceValue( hObj, GetScriptDesc( (T*)NULL ) ) : NULL; +} +#else +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) +#endif + +//----------------------------------------------------------------------------- +// Script scope helper class +//----------------------------------------------------------------------------- + +class CDefScriptScopeBase +{ +public: + static IScriptVM *GetVM() + { + extern IScriptVM *g_pScriptVM; + return g_pScriptVM; + } +}; + +template +class CScriptScopeT : public CDefScriptScopeBase +{ +public: + CScriptScopeT() : + m_hScope( INVALID_HSCRIPT ), + m_flags( 0 ) + { + } + + ~CScriptScopeT() + { + Term(); + } + + bool IsInitialized() + { + return m_hScope != INVALID_HSCRIPT; + } + + bool Init( const char *pszName ) + { + m_hScope = GetVM()->CreateScope( pszName ); + return ( m_hScope != NULL ); + } + + bool Init( HSCRIPT hScope, bool bExternal = true ) + { + if ( bExternal ) + { + m_flags |= EXTERNAL; + } + m_hScope = hScope; + return ( m_hScope != NULL ); + } + + bool InitGlobal() + { + Assert( 0 ); // todo [3/24/2008 tom] + m_hScope = GetVM()->CreateScope( "" ); + return ( m_hScope != NULL ); + } + + void Term() + { + if ( m_hScope != INVALID_HSCRIPT ) + { + IScriptVM *pVM = GetVM(); + if ( pVM ) + { + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + pVM->ReleaseFunction( *m_FuncHandles[i] ); + } + } + m_FuncHandles.Purge(); + if ( m_hScope && pVM && !(m_flags & EXTERNAL) ) + { + pVM->ReleaseScope( m_hScope ); + } + m_hScope = INVALID_HSCRIPT; + } + m_flags = 0; + } + + void InvalidateCachedValues() + { + IScriptVM *pVM = GetVM(); + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + if ( *m_FuncHandles[i] ) + pVM->ReleaseFunction( *m_FuncHandles[i] ); + *m_FuncHandles[i] = INVALID_HSCRIPT; + } + m_FuncHandles.RemoveAll(); + } + + operator HSCRIPT() + { + return ( m_hScope != INVALID_HSCRIPT ) ? m_hScope : NULL; + } + + bool ValueExists( const char *pszKey ) { return GetVM()->ValueExists( m_hScope, pszKey ); } + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return GetVM()->SetValue(m_hScope, pszKey, value ); } + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetVM()->GetValue(m_hScope, pszKey, pValue ); } + void ReleaseValue( ScriptVariant_t &value ) { GetVM()->ReleaseValue( value ); } + bool ClearValue( const char *pszKey) { return GetVM()->ClearValue( m_hScope, pszKey ); } + + ScriptStatus_t Run( HSCRIPT hScript ) + { + InvalidateCachedValues(); + return GetVM()->Run( hScript, m_hScope ); + } + + ScriptStatus_t Run( const char *pszScriptText, const char *pszScriptName = NULL ) + { + InvalidateCachedValues(); + HSCRIPT hScript = GetVM()->CompileScript( pszScriptText, pszScriptName ); + if ( hScript ) + { + ScriptStatus_t result = GetVM()->Run( hScript, m_hScope ); + GetVM()->ReleaseScript( hScript ); + return result; + } + return SCRIPT_ERROR; + } + + ScriptStatus_t Run( const unsigned char *pszScriptText, const char *pszScriptName = NULL ) + { + return Run( (const char *)pszScriptText, pszScriptName); + } + + HSCRIPT LookupFunction( const char *pszFunction ) + { + return GetVM()->LookupFunction( pszFunction, m_hScope ); + } + + void ReleaseFunction( HSCRIPT hScript ) + { + GetVM()->ReleaseFunction( hScript ); + } + + bool FunctionExists( const char *pszFunction ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + GetVM()->ReleaseFunction( hFunction ); + return ( hFunction != NULL ) ; + } + + //----------------------------------------------------- + + enum Flags_t + { + EXTERNAL = 0x01, + }; + + //----------------------------------------------------- + + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn = NULL ) + { + return GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn = NULL ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + +protected: + HSCRIPT m_hScope; + int m_flags; + CUtlVectorConservative m_FuncHandles; +}; + +typedef CScriptScopeT<> CScriptScope; + +#define VScriptAddEnumToScope_( scope, enumVal, scriptName ) (scope).SetValue( scriptName, (int)enumVal ) +#define VScriptAddEnumToScope( scope, enumVal ) VScriptAddEnumToScope_( scope, enumVal, #enumVal ) + +#define VScriptAddEnumToRoot( enumVal ) g_pScriptVM->SetValue( #enumVal, (int)enumVal ) + +#ifdef MAPBASE_VSCRIPT + +// +// Map pointer iteration +// +#define FOR_EACH_MAP_PTR( mapName, iteratorName ) \ + for ( int iteratorName = (mapName)->FirstInorder(); (mapName)->IsUtlMap && iteratorName != (mapName)->InvalidIndex(); iteratorName = (mapName)->NextInorder( iteratorName ) ) + +#define FOR_EACH_MAP_PTR_FAST( mapName, iteratorName ) \ + for ( int iteratorName = 0; (mapName)->IsUtlMap && iteratorName < (mapName)->MaxElement(); ++iteratorName ) if ( !(mapName)->IsValidIndex( iteratorName ) ) continue; else + +#define FOR_EACH_VEC_PTR( vecName, iteratorName ) \ + for ( int iteratorName = 0; iteratorName < (vecName)->Count(); iteratorName++ ) + +//----------------------------------------------------------------------------- + +static void __UpdateScriptHooks( HSCRIPT hooksList ); + +//----------------------------------------------------------------------------- +// +// Keeps track of which events and scopes are hooked without polling this from the script VM on each request. +// Local cache is updated each time there is a change to script hooks: on Add, on Remove, on game restore +// +//----------------------------------------------------------------------------- +class CScriptHookManager +{ +private: + typedef CUtlVector< char* > contextmap_t; + typedef CUtlMap< HScriptRaw, contextmap_t* > scopemap_t; + typedef CUtlMap< char*, scopemap_t* > hookmap_t; + + HSCRIPT m_hfnHookFunc; + + // { [string event], { [HSCRIPT scope], { [string context], [HSCRIPT callback] } } } + hookmap_t m_HookList; + +public: + + CScriptHookManager() : m_HookList( DefLessFunc(char*) ), m_hfnHookFunc(NULL) + { + } + + HSCRIPT GetHookFunction() + { + return m_hfnHookFunc; + } + + // For global hooks + bool IsEventHooked( const char *szEvent ) + { + return m_HookList.Find( const_cast< char* >( szEvent ) ) != m_HookList.InvalidIndex(); + } + + bool IsEventHookedInScope( const char *szEvent, HSCRIPT hScope ) + { + extern IScriptVM *g_pScriptVM; + + Assert( hScope ); + + int eventIdx = m_HookList.Find( const_cast< char* >( szEvent ) ); + if ( eventIdx == m_HookList.InvalidIndex() ) + return false; + + scopemap_t *scopeMap = m_HookList.Element( eventIdx ); + return scopeMap->Find( g_pScriptVM->HScriptToRaw( hScope ) ) != scopeMap->InvalidIndex(); + } + + // + // On VM init, registers script func and caches the hook func. + // + void OnInit() + { + extern IScriptVM *g_pScriptVM; + + ScriptRegisterFunctionNamed( g_pScriptVM, __UpdateScriptHooks, "__UpdateScriptHooks", SCRIPT_HIDE ); + + ScriptVariant_t hHooks; + g_pScriptVM->GetValue( "Hooks", &hHooks ); + + Assert( hHooks.m_type == FIELD_HSCRIPT ); + + if ( hHooks.m_type == FIELD_HSCRIPT ) + { + m_hfnHookFunc = g_pScriptVM->LookupFunction( "Call", hHooks ); + } + + Clear(); + } + + // + // On VM shutdown, clear the cache. + // Not exactly necessary, as the cache will be cleared on VM init next time. + // + void OnShutdown() + { + extern IScriptVM *g_pScriptVM; + + if ( m_hfnHookFunc ) + g_pScriptVM->ReleaseFunction( m_hfnHookFunc ); + + m_hfnHookFunc = NULL; + + Clear(); + } + + // + // On VM restore, update local cache. + // + void OnRestore() + { + extern IScriptVM *g_pScriptVM; + + ScriptVariant_t hHooks; + g_pScriptVM->GetValue( "Hooks", &hHooks ); + + if ( hHooks.m_type == FIELD_HSCRIPT ) + { + // Existing m_hfnHookFunc is invalid + m_hfnHookFunc = g_pScriptVM->LookupFunction( "Call", hHooks ); + + HSCRIPT func = g_pScriptVM->LookupFunction( "__UpdateHooks", hHooks ); + g_pScriptVM->Call( func ); + g_pScriptVM->ReleaseFunction( func ); + g_pScriptVM->ReleaseValue( hHooks ); + } + } + + // + // Clear local cache. + // + void Clear() + { + if ( m_HookList.Count() ) + { + FOR_EACH_MAP_FAST( m_HookList, i ) + { + scopemap_t *scopeMap = m_HookList.Element(i); + + FOR_EACH_MAP_PTR_FAST( scopeMap, j ) + { + contextmap_t *contextMap = scopeMap->Element(j); + contextMap->PurgeAndDeleteElements(); + } + + char *szEvent = m_HookList.Key(i); + free( szEvent ); + + scopeMap->PurgeAndDeleteElements(); + } + + m_HookList.PurgeAndDeleteElements(); + } + } + + // + // Called from script, update local cache. + // + void Update( HSCRIPT hooksList ) + { + extern IScriptVM *g_pScriptVM; + + // Rebuild from scratch + Clear(); + { + ScriptVariant_t varEvent, varScopeMap; + int it = -1; + while ( ( it = g_pScriptVM->GetKeyValue( hooksList, it, &varEvent, &varScopeMap ) ) != -1 ) + { + // types are checked in script + Assert( varEvent.m_type == FIELD_CSTRING ); + Assert( varScopeMap.m_type == FIELD_HSCRIPT ); + + scopemap_t *scopeMap; + + int eventIdx = m_HookList.Find( const_cast< char* >( varEvent.m_pszString ) ); + if ( eventIdx != m_HookList.InvalidIndex() ) + { + scopeMap = m_HookList.Element( eventIdx ); + } + else + { + scopeMap = new scopemap_t( DefLessFunc(HScriptRaw) ); + m_HookList.Insert( strdup( varEvent.m_pszString ), scopeMap ); + } + + ScriptVariant_t varScope, varContextMap; + int it2 = -1; + while ( ( it2 = g_pScriptVM->GetKeyValue( varScopeMap, it2, &varScope, &varContextMap ) ) != -1 ) + { + Assert( varScope.m_type == FIELD_HSCRIPT ); + Assert( varContextMap.m_type == FIELD_HSCRIPT); + + contextmap_t *contextMap; + + int scopeIdx = scopeMap->Find( g_pScriptVM->HScriptToRaw( varScope.m_hScript ) ); + if ( scopeIdx != scopeMap->InvalidIndex() ) + { + contextMap = scopeMap->Element( scopeIdx ); + } + else + { + contextMap = new contextmap_t(); + scopeMap->Insert( g_pScriptVM->HScriptToRaw( varScope.m_hScript ), contextMap ); + } + + ScriptVariant_t varContext, varCallback; + int it3 = -1; + while ( ( it3 = g_pScriptVM->GetKeyValue( varContextMap, it3, &varContext, &varCallback ) ) != -1 ) + { + Assert( varContext.m_type == FIELD_CSTRING ); + Assert( varCallback.m_type == FIELD_HSCRIPT ); + + bool skip = false; + + FOR_EACH_VEC_PTR( contextMap, k ) + { + char *szContext = contextMap->Element(k); + if ( V_strcmp( szContext, varContext.m_pszString ) == 0 ) + { + skip = true; + break; + } + } + + if ( !skip ) + contextMap->AddToTail( strdup( varContext.m_pszString ) ); + + g_pScriptVM->ReleaseValue( varContext ); + g_pScriptVM->ReleaseValue( varCallback ); + } + + g_pScriptVM->ReleaseValue( varScope ); + g_pScriptVM->ReleaseValue( varContextMap ); + } + + g_pScriptVM->ReleaseValue( varEvent ); + g_pScriptVM->ReleaseValue( varScopeMap ); + } + } + } +#ifdef _DEBUG + void Dump() + { + extern IScriptVM *g_pScriptVM; + + FOR_EACH_MAP( m_HookList, i ) + { + scopemap_t *scopeMap = m_HookList.Element(i); + char *szEvent = m_HookList.Key(i); + + Msg( "%s [%p]\n", szEvent, (void*)scopeMap ); + Msg( "{\n" ); + + FOR_EACH_MAP_PTR( scopeMap, j ) + { + HScriptRaw hScope = scopeMap->Key(j); + contextmap_t *contextMap = scopeMap->Element(j); + + Msg( "\t(0x%X) [%p]\n", hScope, (void*)contextMap ); + Msg( "\t{\n" ); + + FOR_EACH_VEC_PTR( contextMap, k ) + { + char *szContext = contextMap->Element(k); + + Msg( "\t\t%-.50s\n", szContext ); + } + + Msg( "\t}\n" ); + } + + Msg( "}\n" ); + } + } +#endif +}; + +inline CScriptHookManager &GetScriptHookManager() +{ + static CScriptHookManager g_ScriptHookManager; + return g_ScriptHookManager; +} + +static void __UpdateScriptHooks( HSCRIPT hooksList ) +{ + GetScriptHookManager().Update( hooksList ); +} + + +//----------------------------------------------------------------------------- +// Function bindings allow script functions to run C++ functions. +// Hooks allow C++ functions to run script functions. +// +// This was previously done with raw function lookups, but Mapbase adds more and +// it's hard to keep track of them without proper standards or documentation. +//----------------------------------------------------------------------------- +struct ScriptHook_t +{ + ScriptFuncDescriptor_t m_desc; + CUtlVector m_pszParameterNames; + bool m_bDefined; + + void AddParameter( const char *pszName, ScriptDataType_t type ) + { + int iCur = m_desc.m_Parameters.Count(); + m_desc.m_Parameters.SetGrowSize( 1 ); m_desc.m_Parameters.EnsureCapacity( iCur + 1 ); m_desc.m_Parameters.AddToTail( type ); + m_pszParameterNames.SetGrowSize( 1 ); m_pszParameterNames.EnsureCapacity( iCur + 1 ); m_pszParameterNames.AddToTail( pszName ); + } + + // ----------------------------------------------------------------- + + // Only valid between CanRunInScope() and Call() + HSCRIPT m_hFunc; + + ScriptHook_t() : + m_hFunc(NULL) + { + } + +#ifdef _DEBUG + // + // An uninitialised script scope will pass as null scope which is considered a valid hook scope (global hook) + // This should catch CanRunInScope() calls without CScriptScope::IsInitalised() checks first. + // + bool CanRunInScope( CScriptScope &hScope ) + { + Assert( hScope.IsInitialized() ); + return hScope.IsInitialized() && CanRunInScope( (HSCRIPT)hScope ); + } +#endif + + // Checks if there's a function of this name which would run in this scope + bool CanRunInScope( HSCRIPT hScope ) + { + // For now, assume null scope (which is used for global hooks) is always hooked + if ( !hScope || GetScriptHookManager().IsEventHookedInScope( m_desc.m_pszScriptName, hScope ) ) + { + m_hFunc = NULL; + return true; + } + + extern IScriptVM *g_pScriptVM; + + // Legacy support if the new system is not being used + m_hFunc = g_pScriptVM->LookupFunction( m_desc.m_pszScriptName, hScope ); + + return !!m_hFunc; + } + + // Call the function + // NOTE: `bRelease` only exists for weapon_custom_scripted legacy script func caching + bool Call( HSCRIPT hScope, ScriptVariant_t *pReturn, ScriptVariant_t *pArgs, bool bRelease = true ) + { + extern IScriptVM *g_pScriptVM; + + // Call() should not be called without CanRunInScope() check first, it caches m_hFunc for legacy support + Assert( CanRunInScope( hScope ) ); + + // Legacy + if ( m_hFunc ) + { + for (int i = 0; i < m_desc.m_Parameters.Count(); i++) + { + g_pScriptVM->SetValue( m_pszParameterNames[i], pArgs[i] ); + } + + ScriptStatus_t status = g_pScriptVM->ExecuteFunction( m_hFunc, NULL, 0, pReturn, hScope, true ); + + if ( bRelease ) + g_pScriptVM->ReleaseFunction( m_hFunc ); + m_hFunc = NULL; + + for (int i = 0; i < m_desc.m_Parameters.Count(); i++) + { + g_pScriptVM->ClearValue( m_pszParameterNames[i] ); + } + + return status == SCRIPT_DONE; + } + // New Hook System + else + { + ScriptStatus_t status = g_pScriptVM->ExecuteHookFunction( m_desc.m_pszScriptName, pArgs, m_desc.m_Parameters.Count(), pReturn, hScope, true ); + return status == SCRIPT_DONE; + } + } +}; +#endif + +//----------------------------------------------------------------------------- +// Script function proxy support +//----------------------------------------------------------------------------- + +class CScriptFuncHolder +{ +public: + CScriptFuncHolder() : hFunction( INVALID_HSCRIPT ) {} + bool IsValid() { return ( hFunction != INVALID_HSCRIPT ); } + bool IsNull() { return ( !hFunction ); } + HSCRIPT hFunction; +}; + +#define DEFINE_SCRIPT_PROXY_GUTS( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < typename RET_TYPE FUNC_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( RET_TYPE *pRetVal FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptVariant_t returnVal; \ + Assert( N == m_desc.m_Parameters.Count() ); \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, &returnVal, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + returnVal.AssignTo( pRetVal ); \ + returnVal.Free(); \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + Assert( N == m_desc.m_Parameters.Count() ); \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0V( FuncName ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + bool FuncName() \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 0 ) +#define DEFINE_SCRIPT_PROXY_1( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 14 ) + +#define DEFINE_SCRIPT_PROXY_1V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 14 ) + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // IVSCRIPT_H diff --git a/public/vscript/vscript_templates.h b/public/vscript/vscript_templates.h new file mode 100644 index 000000000..81e71004c --- /dev/null +++ b/public/vscript/vscript_templates.h @@ -0,0 +1,432 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: VScript Templates +// +//============================================================================= + +#ifndef VSCRIPT_TEMPLATES_H +#define VSCRIPT_TEMPLATES_H + +#include "tier0/basetypes.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#define FUNC_APPEND_PARAMS_0 +#define FUNC_APPEND_PARAMS_1 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 1 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); +#define FUNC_APPEND_PARAMS_2 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 2 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); +#define FUNC_APPEND_PARAMS_3 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 3 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); +#define FUNC_APPEND_PARAMS_4 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 4 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); +#define FUNC_APPEND_PARAMS_5 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 5 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); +#define FUNC_APPEND_PARAMS_6 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 6 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); +#define FUNC_APPEND_PARAMS_7 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 7 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); +#define FUNC_APPEND_PARAMS_8 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 8 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); +#define FUNC_APPEND_PARAMS_9 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 9 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); +#define FUNC_APPEND_PARAMS_10 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 10 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); +#define FUNC_APPEND_PARAMS_11 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 11 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); +#define FUNC_APPEND_PARAMS_12 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 12 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); +#define FUNC_APPEND_PARAMS_13 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 13 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); +#define FUNC_APPEND_PARAMS_14 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 14 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_14 ) ); + +#define DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER ); + +#define DEFINE_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); + +#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } + +#define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } +#define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) +#define ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, scriptName ) ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) +#define ScriptInitMemberFuncDescriptor( pDesc, class, func ) ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, #func ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +inline void *ScriptConvertFuncPtrToVoid( FUNCPTR_TYPE pFunc ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.pFunc = pFunc; + return convert.p; + } +#if defined( _MSC_VER ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } +#elif defined( GNUC ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct GnuMFP + { + union + { + void *funcadr; // If vtable_index_2 is even, then this is the function pointer. + int vtable_index_2; // If vtable_index_2 is odd, then (vtable_index_2 - 1) * 2 is the index into the vtable. + }; + int delta; // Offset from this-ptr to vtable + }; + + GnuMFP *p = (GnuMFP*)&pFunc; + if ( p->delta == 0 ) + { + // No need to check whether this is a direct function pointer or not, + // this gets converted back to a "proper" member-function pointer in + // ScriptConvertFuncPtrFromVoid() to get invoked + return p->funcadr; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } +#else +#error "Need to implement code to crack non-offset member function pointer case" + // For gcc, see: http://www.codeproject.com/KB/cpp/FastDelegate.aspx + // + // Current versions of the GNU compiler use a strange and tricky + // optimization. It observes that, for virtual inheritance, you have to look + // up the vtable in order to get the voffset required to calculate the this + // pointer. While you're doing that, you might as well store the function + // pointer in the vtable. By doing this, they combine the m_func_address and + // m_vtable_index fields into one, and they distinguish between them by + // ensuring that function pointers always point to even addresses but vtable + // indices are always odd: + // + // // GNU g++ uses a tricky space optimisation, also adopted by IBM's VisualAge and XLC. + // struct GnuMFP { + // union { + // CODEPTR funcadr; // always even + // int vtable_index_2; // = vindex*2+1, always odd + // }; + // int delta; + // }; + // adjustedthis = this + delta + // if (funcadr & 1) CALL (* ( *delta + (vindex+1)/2) + 4) + // else CALL funcadr + // + // The G++ method is well documented, so it has been adopted by many other + // vendors, including IBM's VisualAge and XLC compilers, recent versions of + // Open64, Pathscale EKO, and Metrowerks' 64-bit compilers. A simpler scheme + // used by earlier versions of GCC is also very common. SGI's now + // discontinued MIPSPro and Pro64 compilers, and Apple's ancient MrCpp + // compiler used this method. (Note that the Pro64 compiler has become the + // open source Open64 compiler). + +#endif + else + AssertMsg( 0, "Member function pointer not supported. Why on earth are you using virtual inheritance!?" ); + return NULL; +} + +template +inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.p = p; + return convert.pFunc; + } + +#if defined( _MSC_VER ) + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } +#elif defined( GNUC ) + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct GnuMFP + { + union + { + void *funcadr; // If vtable_index_2 is even, then this is the function pointer. + int vtable_index_2; // If vtable_index_2 is odd, then (vtable_index_2 - 1) * 2 is the index into the vtable. + }; + int delta; // Offset from this-ptr to vtable + }; + + union FuncPtrConvertGnu + { + GnuMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertGnu convert; + convert.mfp.funcadr = p; + convert.mfp.delta = 0; + return convert.pFunc; + } +#else +#error "Need to implement code to crack non-offset member function pointer case" +#endif + Assert( 0 ); + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_0 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_1 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_1 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_2 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_2 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_3 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_3 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_4 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_4 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_5 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_5 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_6 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_6 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_7 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_7 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_8 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_8 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_9 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_9 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_10 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_10 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_11 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_11 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_12 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_12 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_13 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_13 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_14 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_14 + +#define SCRIPT_BINDING_ARGS_0 +#define SCRIPT_BINDING_ARGS_1 pArguments[0] +#define SCRIPT_BINDING_ARGS_2 pArguments[0], pArguments[1] +#define SCRIPT_BINDING_ARGS_3 pArguments[0], pArguments[1], pArguments[2] +#define SCRIPT_BINDING_ARGS_4 pArguments[0], pArguments[1], pArguments[2], pArguments[3] +#define SCRIPT_BINDING_ARGS_5 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4] +#define SCRIPT_BINDING_ARGS_6 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5] +#define SCRIPT_BINDING_ARGS_7 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6] +#define SCRIPT_BINDING_ARGS_8 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7] +#define SCRIPT_BINDING_ARGS_9 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8] +#define SCRIPT_BINDING_ARGS_10 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9] +#define SCRIPT_BINDING_ARGS_11 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10] +#define SCRIPT_BINDING_ARGS_12 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11] +#define SCRIPT_BINDING_ARGS_13 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12] +#define SCRIPT_BINDING_ARGS_14 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12], pArguments[13] + + +#define DEFINE_SCRIPT_BINDINGS(N) \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || !pReturn || pContext ) \ + { \ + return false; \ + } \ + *pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || pReturn || pContext ) \ + { \ + return false; \ + } \ + ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || !pReturn || !pContext ) \ + { \ + return false; \ + } \ + *pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || pReturn || !pContext ) \ + { \ + return false; \ + } \ + (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CNonMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } + +FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#endif // VSCRIPT_TEMPLATES_H diff --git a/raytrace/raytrace.cpp b/raytrace/raytrace.cpp index 4acad9ab5..db85bc173 100644 --- a/raytrace/raytrace.cpp +++ b/raytrace/raytrace.cpp @@ -233,7 +233,9 @@ void CacheOptimizedTriangle::ChangeIntoIntersectionFormat(void) } +#ifndef MAPBASE int n_intersection_calculations=0; +#endif // MAPBASE int CacheOptimizedTriangle::ClassifyAgainstAxisSplit(int split_plane, float split_value) { @@ -368,8 +370,8 @@ void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 T TMin=MaxSIMD(TMin,MinSIMD(isect_min_t,isect_max_t)); TMax=MinSIMD(TMax,MaxSIMD(isect_min_t,isect_max_t)); } - fltx4 active=CmpLeSIMD(TMin,TMax); // mask of which rays are active - if (! IsAnyNegative(active) ) + fltx4 activeLocl=CmpLeSIMD(TMin,TMax); // mask of which rays are active + if (! IsAnyNegative(activeLocl) ) return; // missed bounding box int32 mailboxids[MAILBOX_HASH_SIZE]; // used to avoid redundant triangle tests @@ -423,11 +425,11 @@ void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 T MulSIMD( SubSIMD(ReplicateX4(CurNode->SplittingPlaneValue), rays.origin[split_plane_number]),OneOverRayDir[split_plane_number]); - active=CmpLeSIMD(TMin,TMax); // mask of which rays are active + activeLocl=CmpLeSIMD(TMin,TMax); // mask of which rays are active // now, decide how to traverse children. can either do front,back, or do front and push // back. - fltx4 hits_front=AndSIMD(active,CmpGeSIMD(dist_to_sep_plane,TMin)); + fltx4 hits_front=AndSIMD(activeLocl,CmpGeSIMD(dist_to_sep_plane,TMin)); if (! IsAnyNegative(hits_front)) { // missed the front. only traverse back @@ -476,7 +478,9 @@ void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 T TriIntersectData_t const *tri = &( OptimizedTriangleList[tnum].m_Data.m_IntersectData ); if ( ( mailboxids[mbox_slot] != tnum ) && ( tri->m_nTriangleID != skip_id ) ) { +#ifndef MABASE n_intersection_calculations++; +#endif // MAPBASE mailboxids[mbox_slot] = tnum; // compute plane intersection diff --git a/responserules/runtime/criteriaset.cpp b/responserules/runtime/criteriaset.cpp new file mode 100644 index 000000000..6d53e9545 --- /dev/null +++ b/responserules/runtime/criteriaset.cpp @@ -0,0 +1,479 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "rrbase.h" + +#include "utlmap.h" + +#include "tier1/mapbase_con_groups.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace ResponseRules; + +//----------------------------------------------------------------------------- +// Case-insensitive criteria symbol table +//----------------------------------------------------------------------------- +CUtlSymbolTable CriteriaSet::sm_CriteriaSymbols( 1024, 1024, true ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *raw - +// *key - +// keylen - +// *value - +// valuelen - +// *duration - +// Output : static bool +//----------------------------------------------------------------------------- +const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration, const char *entireContext ) +{ + char *colon1 = Q_strstr( raw, ":" ); + if ( !colon1 ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "SplitContext: warning, ignoring context '%s', missing colon separator!\n", raw ); + *key = *value = 0; + return NULL; + } + + int len = colon1 - raw; + Q_strncpy( key, raw, MIN( len + 1, keylen ) ); + key[ MIN( len, keylen - 1 ) ] = 0; + + bool last = false; + char *end = Q_strstr( colon1 + 1, "," ); + if ( !end ) + { + int remaining = Q_strlen( colon1 + 1 ); + end = colon1 + 1 + remaining; + last = true; + } + + char *colon2 = Q_strstr( colon1 + 1, ":" ); + if ( colon2 && ( colon2 < end ) ) + { + if ( duration ) + *duration = atof( colon2 + 1 ); + + char durationStartChar = *(colon2 + 1); + if ( durationStartChar < '0' || durationStartChar > '9' ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "SplitContext: warning, ignoring context '%s', missing comma separator! Entire context was '%s'.\n", raw, entireContext ); + *key = *value = 0; + return NULL; + } + + len = MIN( colon2 - ( colon1 + 1 ), valuelen - 1 ); + Q_strncpy( value, colon1 + 1, len + 1 ); + value[ len ] = 0; + } + else + { + if ( duration ) + *duration = 0.0; + + len = MIN( end - ( colon1 + 1 ), valuelen - 1 ); + Q_strncpy( value, colon1 + 1, len + 1 ); + value[ len ] = 0; + } + + return last ? NULL : end + 1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CriteriaSet::CriteriaSet() : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_bOverrideOnAppend(true), + m_nNumPrefixedContexts(0) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CriteriaSet::CriteriaSet( const CriteriaSet& src ) : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_nNumPrefixedContexts(src.m_nNumPrefixedContexts) +{ + m_Lookup.EnsureCapacity( src.m_Lookup.Count() ); + for ( short i = src.m_Lookup.FirstInorder(); + i != src.m_Lookup.InvalidIndex(); + i = src.m_Lookup.NextInorder( i ) ) + { + m_Lookup.Insert( src.m_Lookup[ i ] ); + } +} + +CriteriaSet::CriteriaSet( const char *criteria, const char *value ) : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_bOverrideOnAppend(true) +{ + AppendCriteria(criteria,value); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CriteriaSet::~CriteriaSet() +{ +} + +//----------------------------------------------------------------------------- +// Computes a symbol for the criteria +//----------------------------------------------------------------------------- +CriteriaSet::CritSymbol_t CriteriaSet::ComputeCriteriaSymbol( const char *criteria ) +{ + return sm_CriteriaSymbols.AddString( criteria ); +} + + +//----------------------------------------------------------------------------- +// Computes a symbol for the criteria +//----------------------------------------------------------------------------- +void CriteriaSet::AppendCriteria( CriteriaSet::CritSymbol_t criteria, const char *value, float weight ) +{ + int idx = FindCriterionIndex( criteria ); + if ( idx == -1 ) + { + CritEntry_t entry; + entry.criterianame = criteria; + MEM_ALLOC_CREDIT(); + idx = m_Lookup.Insert( entry ); + if ( sm_CriteriaSymbols.String(criteria)[0] == kAPPLYTOWORLDPREFIX ) + { + m_nNumPrefixedContexts += 1; + } + } + else // criteria already existed + { + // bail out if override existing criteria is not allowed + if ( !m_bOverrideOnAppend ) + return; + } + + CritEntry_t *entry = &m_Lookup[ idx ]; + entry->SetValue( value ); + entry->weight = weight; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criteria - +// "" - +// 1.0f - +//----------------------------------------------------------------------------- +void CriteriaSet::AppendCriteria( const char *pCriteriaName, const char *value /*= ""*/, float weight /*= 1.0f*/ ) +{ + CUtlSymbol criteria = ComputeCriteriaSymbol( pCriteriaName ); + AppendCriteria( criteria, value, weight ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criteria - +// "" - +// 1.0f - +//----------------------------------------------------------------------------- +void CriteriaSet::AppendCriteria( const char *criteria, float value, float weight /*= 1.0f*/ ) +{ + char buf[32]; + V_snprintf( buf, 32, "%f", value ); + AppendCriteria( criteria, buf, weight ); +} + + +//----------------------------------------------------------------------------- +// Removes criteria in a set +//----------------------------------------------------------------------------- +void CriteriaSet::RemoveCriteria( const char *criteria ) +{ + const int idx = FindCriterionIndex( criteria ); + if ( idx == -1 ) + return; + + if ( criteria[0] == kAPPLYTOWORLDPREFIX ) + { + Assert( m_nNumPrefixedContexts > 0 ); + m_nNumPrefixedContexts = isel( m_nNumPrefixedContexts - 1, m_nNumPrefixedContexts - 1, 0 ); + } + RemoveCriteria( idx, false ); +} + +// bTestForIndex tells us whether the calling function has already checked for a +// $ prefix and decremented m_nNumPrefixedContexts appropriately (false), +// or if this function should do that (true). +void CriteriaSet::RemoveCriteria( int idx, bool bTestForPrefix ) +{ + Assert( m_Lookup.IsValidIndex(idx) ); + if ( bTestForPrefix ) + { + if ( sm_CriteriaSymbols.String( m_Lookup[idx].criterianame )[0] == kAPPLYTOWORLDPREFIX ) + { + Assert( m_nNumPrefixedContexts > 0 ); + m_nNumPrefixedContexts = isel( m_nNumPrefixedContexts - 1, m_nNumPrefixedContexts - 1, 0 ); + } + } + m_Lookup.RemoveAt( idx ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CriteriaSet::GetCount() const +{ + return m_Lookup.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : int +//----------------------------------------------------------------------------- +int CriteriaSet::FindCriterionIndex( CritSymbol_t criteria ) const +{ + CritEntry_t search; + search.criterianame = criteria; + int idx = m_Lookup.Find( search ); + return ( idx == m_Lookup.InvalidIndex() ) ? -1 : idx; +} + +int CriteriaSet::FindCriterionIndex( const char *name ) const +{ + CUtlSymbol criteria = ComputeCriteriaSymbol( name ); + return FindCriterionIndex( criteria ); +} + + +//----------------------------------------------------------------------------- +// Returns the name symbol +//----------------------------------------------------------------------------- +CriteriaSet::CritSymbol_t CriteriaSet::GetNameSymbol( int nIndex ) const +{ + if ( nIndex < 0 || nIndex >= (int)m_Lookup.Count() ) + return UTL_INVAL_SYMBOL; + + const CritEntry_t *entry = &m_Lookup[ nIndex ]; + return entry->criterianame; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : char const +//----------------------------------------------------------------------------- +const char *CriteriaSet::GetName( int index ) const +{ + if ( index < 0 || index >= (int)m_Lookup.Count() ) + return ""; + else + { + const char *pCriteriaName = sm_CriteriaSymbols.String( m_Lookup[ index ].criterianame ); + return pCriteriaName ? pCriteriaName : ""; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : char const +//----------------------------------------------------------------------------- +const char *CriteriaSet::GetValue( int index ) const +{ + if ( index < 0 || index >= (int)m_Lookup.Count() ) + return ""; + + const CritEntry_t *entry = &m_Lookup[ index ]; + return entry->value ? entry->value : ""; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : float +//----------------------------------------------------------------------------- +float CriteriaSet::GetWeight( int index ) const +{ + if ( index < 0 || index >= (int)m_Lookup.Count() ) + return 1.0f; + + const CritEntry_t *entry = &m_Lookup[ index ]; + return entry->weight; +} + + +//----------------------------------------------------------------------------- +// Purpose: Merge another criteria set into this one. +//----------------------------------------------------------------------------- +void CriteriaSet::Merge( const CriteriaSet * RESTRICT otherCriteria ) +{ + Assert(otherCriteria); + if (!otherCriteria) + return; + + // for now, just duplicate everything. + int count = otherCriteria->GetCount(); + EnsureCapacity( count + GetCount() ); + for ( int i = 0 ; i < count ; ++i ) + { + AppendCriteria( otherCriteria->GetNameSymbol(i), otherCriteria->GetValue(i), otherCriteria->GetWeight(i) ); + } +} + +void CriteriaSet::Merge( const char *modifiers ) // add criteria parsed from a text string +{ + // Always include any optional modifiers + if ( modifiers == NULL ) + return; + + char copy_modifiers[ 255 ]; + const char *pCopy; + char key[ 128 ] = { 0 }; + char value[ 128 ] = { 0 }; + + Q_strncpy( copy_modifiers, modifiers, sizeof( copy_modifiers ) ); + pCopy = copy_modifiers; + + while( pCopy ) + { + pCopy = SplitContext( pCopy, key, sizeof( key ), value, sizeof( value ), NULL, modifiers ); + + if( *key && *value ) + { + AppendCriteria( key, value, 1 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CriteriaSet::Describe() const +{ + // build an alphabetized representation of the set for printing + typedef CUtlMap tMap; + tMap m_TempMap( 0, m_Lookup.Count(), CaselessStringLessThan ); + + for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) ) + { + const CritEntry_t *entry = &m_Lookup[ i ]; + + m_TempMap.Insert( sm_CriteriaSymbols.String( entry->criterianame ), entry ); + } + + for ( tMap::IndexType_t i = m_TempMap.FirstInorder(); i != m_TempMap.InvalidIndex(); i = m_TempMap.NextInorder( i ) ) + { + // const CritEntry_t *entry = &m_TempMap[ i ]; + // const char *pCriteriaName = sm_CriteriaSymbols.String( entry->criterianame ); + + const char *name = m_TempMap.Key( i ); + const CritEntry_t *entry = m_TempMap.Element( i ); + if ( entry->weight != 1.0f ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s' (weight %f)\n", name, entry->value ? entry->value : "", entry->weight ); + } + else + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s'\n", name, entry->value ? entry->value : "" ); + } + } + + /* + for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) ) + { + const CritEntry_t *entry = &m_Lookup[ i ]; + + const char *pCriteriaName = sm_CriteriaSymbols.String( entry->criterianame ); + if ( entry->weight != 1.0f ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s' (weight %f)\n", pCriteriaName, entry->value ? entry->value : "", entry->weight ); + } + else + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " %20s = '%s'\n", pCriteriaName, entry->value ? entry->value : "" ); + } + } + */ +} + + +void CriteriaSet::Reset() +{ + m_Lookup.Purge(); +} + +void CriteriaSet::WriteToEntity( CBaseEntity *pEntity ) +{ +#if 0 + if ( GetCount() < 1 ) + return; + + for ( int i = Head() ; IsValidIndex(i); i = Next(i) ) + { + pEntity->AddContext( GetName(i), GetValue(i), 0 ); + } +#else + AssertMsg( false, "CriteriaSet::WriteToEntity has not been ported from l4d2.\n" ); +#endif +} + +int CriteriaSet::InterceptWorldSetContexts( CriteriaSet * RESTRICT pFrom, CriteriaSet * RESTRICT pSetOnWorld ) +{ + // Assert( pFrom ); Assert( pTo ); Assert( pSetOnWorld ); + Assert( pSetOnWorld != pFrom ); + Assert( pSetOnWorld->GetCount() == 0 ); + + if ( pFrom->m_nNumPrefixedContexts == 0 ) + { + // nothing needs to be done to it. + return 0; + } + +#ifdef DEBUG + // save this off for later error checking. + const int nPrefixedContexts = pFrom->m_nNumPrefixedContexts; +#endif + + // make enough space for the expected output quantity. + pSetOnWorld->EnsureCapacity( pFrom->m_nNumPrefixedContexts ); + + // initialize a buffer with the "world" prefix (so we can use strncpy instead of snprintf and be much faster) + char buf[80] = { 'w', 'o', 'r', 'l', 'd', '\0' }; + const unsigned int PREFIXLEN = 5; // strlen("world") + + // create a second tree that has the appropriately renamed criteria, + // then swap it into pFrom + CriteriaSet rewrite; + rewrite.EnsureCapacity( pFrom->GetCount() + 1 ); + + for ( int i = pFrom->Head(); pFrom->IsValidIndex(i); i = pFrom->Next(i) ) + { + const char *pszName = pFrom->GetName( i ); + if ( pszName[0] == CriteriaSet::kAPPLYTOWORLDPREFIX ) + { // redirect to the world contexts + V_strncpy( buf+PREFIXLEN, pszName+1, sizeof(buf) - PREFIXLEN ); + rewrite.AppendCriteria( buf, pFrom->GetValue(i), pFrom->GetWeight(i) ); + pSetOnWorld->AppendCriteria( pszName+1, pFrom->GetValue(i), pFrom->GetWeight(i) ); + buf[PREFIXLEN] = 0; + } + else + { // does not need to be fiddled; do not write back to world + rewrite.AppendCriteria( pFrom->GetNameSymbol(i), pFrom->GetValue(i), pFrom->GetWeight(i) ); + } + } + AssertMsg2( pSetOnWorld->GetCount() == nPrefixedContexts, "Count of $ persistent RR contexts is inconsistent (%d vs %d)! Call Elan.", + pSetOnWorld->GetCount(), nPrefixedContexts ); + + pFrom->m_nNumPrefixedContexts = 0; + pFrom->m_Lookup.Swap(rewrite.m_Lookup); + return pSetOnWorld->GetCount(); +} diff --git a/responserules/runtime/response_rules.vpc b/responserules/runtime/response_rules.vpc new file mode 100644 index 000000000..9ab73afaa --- /dev/null +++ b/responserules/runtime/response_rules.vpc @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// response_rules.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR "..\.." +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;..\public\responserules" + $PreprocessorDefinitions "$BASE;RR_RUNTIME" + } +} + +$Project "responserules_runtime" +{ + $Folder "Source Files" + { + $File "criteriaset.cpp" + $File "response_system.cpp" + $File "response_system.h" + $File "response_types.cpp" + $File "response_types_internal.cpp" + $File "response_types_internal.h" + $File "rr_convars.cpp" + $File "rr_response.cpp" + $File "rr_speechconcept.cpp" + $File "rrrlib.cpp" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\responserules\response_host_interface.h" + $File "$SRCDIR\public\responserules\response_types.h" + $File "$SRCDIR\public\responserules\rr_speechconcept.h" + } +} \ No newline at end of file diff --git a/responserules/runtime/response_system.cpp b/responserules/runtime/response_system.cpp new file mode 100644 index 000000000..7ae9276c7 --- /dev/null +++ b/responserules/runtime/response_system.cpp @@ -0,0 +1,2959 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" +#include "vstdlib/random.h" +#include "utlbuffer.h" +#include "tier1/interval.h" +#include "convar.h" +#include "fmtstr.h" +#include "generichash.h" +#include "tier1/mapbase_con_groups.h" +#ifdef MAPBASE +#include "tier1/mapbase_matchers_base.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// #pragma optimize( "", off ) + +using namespace ResponseRules; +static void CC_RR_Debug_ResponseConcept_Exclude( const CCommand &args ); +static ConCommand rr_debug_responseconcept_exclude( "rr_debugresponseconcept_exclude", CC_RR_Debug_ResponseConcept_Exclude, "Set a list of concepts to exclude from rr_debugresponseconcept. Separate multiple concepts with spaces. Call with no arguments to see current list. Call 'rr_debug_responseconcept_exclude !' to reset."); + +namespace ResponseRules +{ + // ick + // Wrap string lookup with a hash on the string so that all of the repetitive playerxxx type strings get bucketed out better + #define STRING_BUCKETS_COUNT 64 // Must be power of two + #define STRING_BUCKETS_MASK ( STRING_BUCKETS_COUNT - 1 ) + + CUtlRBTree g_ResponseStrings[ STRING_BUCKETS_COUNT ]; + class CResponseStringBuckets + { + public: + CResponseStringBuckets() + { + for ( int i = 0; i < ARRAYSIZE( g_ResponseStrings ); ++i ) + { + g_ResponseStrings[ i ].SetLessFunc( &StringLessThan ); + } + } + } g_ReponseStringBucketInitializer; + + const char *ResponseCopyString( const char *in ) + { + if ( !in ) + return NULL; + if ( !*in ) + return ""; + + int bucket = ( RR_HASH( in ) & STRING_BUCKETS_MASK ); + + CUtlRBTree &list = g_ResponseStrings[ bucket ]; + + int i = list.Find( in ); + if ( i != list.InvalidIndex() ) +{ + return list[i]; + } + + int len = Q_strlen( in ); + char *out = new char[ len + 1 ]; + Q_memcpy( out, in, len ); + out[ len ] = 0; + list.Insert( out ); + return out; + } +} + +IEngineEmulator *IEngineEmulator::Get() +{ + AssertMsg( IEngineEmulator::s_pSingleton, "Response rules fail: no IEngineEmulator" ); + return IEngineEmulator::s_pSingleton; +} + + +//----------------------------------------------------------------------------- +// Convars +//----------------------------------------------------------------------------- + + +ConVar rr_debugresponses( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring, 3 for noisy). If set to 4, it will only show response success/failure for npc_selected NPCs." ); +ConVar rr_debugrule( "rr_debugrule", "", FCVAR_NONE, "If set to the name of the rule, that rule's score will be shown whenever a concept is passed into the response rules system."); +ConVar rr_dumpresponses( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" ); +ConVar rr_debugresponseconcept( "rr_debugresponseconcept", "", FCVAR_NONE, "If set, rr_debugresponses will print only responses testing for the specified concept" ); +#define RR_DEBUGRESPONSES_SPECIALCASE 4 + +#ifdef MAPBASE +ConVar rr_disableemptyrules( "rr_disableemptyrules", "0", FCVAR_NONE, "Disables rules with no remaining responses, e.g. rules which use norepeat responses." ); +#endif + + + +//----------------------------------------------------------------------------- +// Copied from SoundParametersInternal.cpp +//----------------------------------------------------------------------------- + +#define SNDLVL_PREFIX "SNDLVL_" + +struct SoundLevelLookup +{ + soundlevel_t level; + char const *name; +}; + +// NOTE: Needs to reflect the soundlevel_t enum defined in soundflags.h +static SoundLevelLookup g_pSoundLevels[] = +{ + { SNDLVL_NONE, "SNDLVL_NONE" }, + { SNDLVL_20dB, "SNDLVL_20dB" }, + { SNDLVL_25dB, "SNDLVL_25dB" }, + { SNDLVL_30dB, "SNDLVL_30dB" }, + { SNDLVL_35dB, "SNDLVL_35dB" }, + { SNDLVL_40dB, "SNDLVL_40dB" }, + { SNDLVL_45dB, "SNDLVL_45dB" }, + { SNDLVL_50dB, "SNDLVL_50dB" }, + { SNDLVL_55dB, "SNDLVL_55dB" }, + { SNDLVL_IDLE, "SNDLVL_IDLE" }, + { SNDLVL_TALKING, "SNDLVL_TALKING" }, + { SNDLVL_60dB, "SNDLVL_60dB" }, + { SNDLVL_65dB, "SNDLVL_65dB" }, + { SNDLVL_STATIC, "SNDLVL_STATIC" }, + { SNDLVL_70dB, "SNDLVL_70dB" }, + { SNDLVL_NORM, "SNDLVL_NORM" }, + { SNDLVL_75dB, "SNDLVL_75dB" }, + { SNDLVL_80dB, "SNDLVL_80dB" }, + { SNDLVL_85dB, "SNDLVL_85dB" }, + { SNDLVL_90dB, "SNDLVL_90dB" }, + { SNDLVL_95dB, "SNDLVL_95dB" }, + { SNDLVL_100dB, "SNDLVL_100dB" }, + { SNDLVL_105dB, "SNDLVL_105dB" }, + { SNDLVL_110dB, "SNDLVL_110dB" }, + { SNDLVL_120dB, "SNDLVL_120dB" }, + { SNDLVL_130dB, "SNDLVL_130dB" }, + { SNDLVL_GUNFIRE, "SNDLVL_GUNFIRE" }, + { SNDLVL_140dB, "SNDLVL_140dB" }, + { SNDLVL_150dB, "SNDLVL_150dB" }, + { SNDLVL_180dB, "SNDLVL_180dB" }, +}; + +static soundlevel_t TextToSoundLevel( const char *key ) +{ + if ( !key ) + { + Assert( 0 ); + return SNDLVL_NORM; + } + + int c = ARRAYSIZE( g_pSoundLevels ); + + int i; + + for ( i = 0; i < c; i++ ) + { + SoundLevelLookup *entry = &g_pSoundLevels[ i ]; + if ( !Q_strcasecmp( key, entry->name ) ) + return entry->level; + } + + if ( !Q_strnicmp( key, SNDLVL_PREFIX, Q_strlen( SNDLVL_PREFIX ) ) ) + { + char const *val = key + Q_strlen( SNDLVL_PREFIX ); + int sndlvl = atoi( val ); + if ( sndlvl > 0 && sndlvl <= 180 ) + { + return ( soundlevel_t )sndlvl; + } + } + + DevMsg( "CSoundEmitterSystem: Unknown sound level %s\n", key ); + + return SNDLVL_NORM; +} + +CResponseSystem::ExcludeList_t CResponseSystem::m_DebugExcludeList( 4, 0 ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CResponseSystem::CResponseSystem() : + m_RootCommandHashes( 0, 0, DefLessFunc( unsigned int ) ), + m_FileDispatch( 0, 0, DefLessFunc( unsigned int ) ), + m_RuleDispatch( 0, 0, DefLessFunc( unsigned int ) ), + m_ResponseDispatch( 0, 0, DefLessFunc( unsigned int ) ), + m_ResponseGroupDispatch( 0, 0, DefLessFunc( unsigned int ) ) +{ + token[0] = 0; + m_bUnget = false; + m_bCustomManagable = false; +#ifdef MAPBASE + m_bInProspective = false; +#endif + + BuildDispatchTables(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CResponseSystem::~CResponseSystem() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CResponseSystem::GetCurrentScript( char *buf, size_t buflen ) +{ + Assert( buf ); + buf[ 0 ] = 0; + if ( m_ScriptStack.Count() <= 0 ) + return; + + if ( IEngineEmulator::Get()->GetFilesystem()->String( m_ScriptStack[ 0 ].name, buf, buflen ) ) + { + return; + } + buf[ 0 ] = 0; +} + +void CResponseSystem::PushScript( const char *scriptfile, unsigned char *buffer ) +{ + ScriptEntry e; + e.name = IEngineEmulator::Get()->GetFilesystem()->FindOrAddFileName( scriptfile ); + e.buffer = buffer; + e.currenttoken = (char *)e.buffer; + e.tokencount = 0; + m_ScriptStack.AddToHead( e ); +} + +void CResponseSystem::PopScript(void) +{ + Assert( m_ScriptStack.Count() >= 1 ); + if ( m_ScriptStack.Count() <= 0 ) + return; + + m_ScriptStack.Remove( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::Clear() +{ + m_Responses.RemoveAll(); + m_Criteria.RemoveAll(); +#ifdef MAPBASE + // Must purge to avoid issues with reloading the system + m_RulePartitions.PurgeAndDeleteElements(); +#else + m_RulePartitions.RemoveAll(); +#endif + m_Enumerations.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// found - +// Output : float +//----------------------------------------------------------------------------- +float CResponseSystem::LookupEnumeration( const char *name, bool& found ) +{ + int idx = m_Enumerations.Find( name ); + if ( idx == m_Enumerations.InvalidIndex() ) + { + found = false; + return 0.0f; + } + + + found = true; + return m_Enumerations[ idx ].value; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : matcher - +//----------------------------------------------------------------------------- +void CResponseSystem::ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ) +{ + if ( rawtoken[0] != '[' ) + { + Q_strncpy( token, rawtoken, bufsize ); + return; + } + + // Now lookup enumeration + bool found = false; + float f = LookupEnumeration( rawtoken, found ); + if ( !found ) + { + Q_strncpy( token, rawtoken, bufsize ); + ResponseWarning( "No such enumeration '%s'\n", token ); + return; + } + + Q_snprintf( token, bufsize, "%f", f ); +} + + +#ifndef MAPBASE // Already in mapbase_matchers_base +static bool AppearsToBeANumber( char const *token ) +{ + if ( atof( token ) != 0.0f ) + return true; + + char const *p = token; + while ( *p ) + { + if ( *p != '0' ) + return false; + + p++; + } + + return true; +} +#endif + +void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) +{ + const char *s = c->value; + if ( !s ) + { + matcher.valid = false; + return; + } + + const char *in = s; + + char token[ 128 ]; + char rawtoken[ 128 ]; + + token[ 0 ] = 0; + rawtoken[ 0 ] = 0; + + int n = 0; + + bool gt = false; + bool lt = false; + bool eq = false; + bool nt = false; +#ifdef MAPBASE + bool bit = false; +#endif + + bool done = false; + while ( !done ) + { + switch( *in ) + { + case '>': + { + gt = true; + Assert( !lt ); // Can't be both + } + break; + case '<': + { + lt = true; + Assert( !gt ); // Can't be both + } + break; + case '=': + { + eq = true; + } + break; + case ',': + case '\0': + { + rawtoken[ n ] = 0; + n = 0; + + // Convert raw token to real token in case token is an enumerated type specifier + ResolveToken( matcher, token, sizeof( token ), rawtoken ); + +#ifdef MAPBASE + // Bits are an entirely different and independent story + if (bit) + { + matcher.isbit = true; + matcher.notequal = nt; + + matcher.isnumeric = true; + } + else +#endif + // Fill in first data set + if ( gt ) + { + matcher.usemin = true; + matcher.minequals = eq; + matcher.minval = (float)atof( token ); + + matcher.isnumeric = true; + } + else if ( lt ) + { + matcher.usemax = true; + matcher.maxequals = eq; + matcher.maxval = (float)atof( token ); + + matcher.isnumeric = true; + } + else + { + if ( *in == ',' ) + { + // If there's a comma, this better have been a less than or a gt key + Assert( 0 ); + } + + matcher.notequal = nt; + + matcher.isnumeric = AppearsToBeANumber( token ); + } + + gt = lt = eq = nt = false; + + if ( !(*in) ) + { + done = true; + } + } + break; + case '!': + nt = true; + break; +#ifdef MAPBASE + case '~': + nt = true; + case '&': + bit = true; + break; +#endif + default: + rawtoken[ n++ ] = *in; + break; + } + + in++; + } + + matcher.SetToken( token ); + matcher.SetRaw( rawtoken ); + matcher.valid = true; +} + +bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose /*=false*/ ) +{ + if ( !m.valid ) + return false; + + float v = (float)atof( setValue ); + if ( setValue[0] == '[' ) + { + bool found = false; + v = LookupEnumeration( setValue, found ); + } + +#ifdef MAPBASE + // Bits are always a different story + if (m.isbit) + { + int v1 = v; + int v2 = atoi( m.GetToken() ); + if (m.notequal) + return (v1 & v2) == 0; + else + return (v1 & v2) != 0; + } +#endif + + int minmaxcount = 0; + + if ( m.usemin ) + { + if ( m.minequals ) + { + if ( v < m.minval ) + return false; + } + else + { + if ( v <= m.minval ) + return false; + } + + ++minmaxcount; + } + + if ( m.usemax ) + { + if ( m.maxequals ) + { + if ( v > m.maxval ) + return false; + } + else + { + if ( v >= m.maxval ) + return false; + } + + ++minmaxcount; + } + + // Had one or both criteria and met them + if ( minmaxcount >= 1 ) + { + return true; + } + + if ( m.notequal ) + { + if ( m.isnumeric ) + { + if ( v == (float)atof( m.GetToken() ) ) + return false; + } + else + { +#ifdef MAPBASE + if ( Matcher_NamesMatch( m.GetToken(), setValue ) ) +#else + if ( !Q_stricmp( setValue, m.GetToken() ) ) +#endif + return false; + } + + return true; + } + + if ( m.isnumeric ) + { + // If the setValue is "", the NPC doesn't have the key at all, + // in which case we shouldn't match "0". + if ( !setValue || !setValue[0] ) + return false; + + return v == (float)atof( m.GetToken() ); + } + +#ifdef MAPBASE + return Matcher_NamesMatch( m.GetToken(), setValue ); +#else + return !Q_stricmp( setValue, m.GetToken() ) ? true : false; +#endif +} + +bool CResponseSystem::Compare( const char *setValue, Criteria *c, bool verbose /*= false*/ ) +{ + Assert( c ); + Assert( setValue ); + + bool bret = CompareUsingMatcher( setValue, c->matcher, verbose ); + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "'%20s' vs. '%20s' = ", setValue, c->value ); + + { + //DevMsg( "\n" ); + //m.Describe(); + } + } + return bret; +} + +float CResponseSystem::RecursiveScoreSubcriteriaAgainstRule( const CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ) +{ + float score = 0.0f; + int subcount = parent->subcriteria.Count(); + for ( int i = 0; i < subcount; i++ ) + { + int icriterion = parent->subcriteria[ i ]; + + bool excludesubrule = false; + if (verbose) + { + DevMsg( "\n" ); + } + score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, excludesubrule, verbose ); + } + + exclude = ( parent->required && score == 0.0f ) ? true : false; + + return score * parent->weight.GetFloat(); +} + +float CResponseSystem::RecursiveLookForCriteria( const CriteriaSet &criteriaSet, Criteria *pParent ) +{ + float flScore = 0.0f; + int nSubCount = pParent->subcriteria.Count(); + for ( int iSub = 0; iSub < nSubCount; ++iSub ) + { + int iCriteria = pParent->subcriteria[iSub]; + flScore += LookForCriteria( criteriaSet, iCriteria ); + } + + return flScore; +} + +float CResponseSystem::LookForCriteria( const CriteriaSet &criteriaSet, int iCriteria ) +{ + Criteria *pCriteria = &m_Criteria[iCriteria]; + if ( pCriteria->IsSubCriteriaType() ) + { + return RecursiveLookForCriteria( criteriaSet, pCriteria ); + } + + int iIndex = criteriaSet.FindCriterionIndex( pCriteria->nameSym ); + if ( iIndex == -1 ) + return 0.0f; + + Assert( criteriaSet.GetValue( iIndex ) ); + if ( Q_stricmp( criteriaSet.GetValue( iIndex ), pCriteria->value ) ) + return 0.0f; + + return 1.0f; +} + +float CResponseSystem::ScoreCriteriaAgainstRuleCriteria( const CriteriaSet& set, int icriterion, bool& exclude, bool verbose /*=false*/ ) +{ + Criteria *c = &m_Criteria[ icriterion ]; + + if ( c->IsSubCriteriaType() ) + { + return RecursiveScoreSubcriteriaAgainstRule( set, c, exclude, verbose ); + } + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " criterion '%25s':'%15s' ", m_Criteria.GetElementName( icriterion ), CriteriaSet::SymbolToStr(c->nameSym) ); + } + + exclude = false; + + float score = 0.0f; + + const char *actualValue = ""; + + /* + const char * RESTRICT critname = c->name; + CUtlSymbol sym(critname); + const char * nameDoubleCheck = sym.String(); + */ + int found = set.FindCriterionIndex( c->nameSym ); + if ( found != -1 ) + { + actualValue = set.GetValue( found ); + if ( !actualValue ) + { + Assert( 0 ); + return score; + } + } + + Assert( actualValue ); + + if ( Compare( actualValue, c, verbose ) ) + { + float w = set.GetWeight( found ); + score = w * c->weight.GetFloat(); + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "matched, weight %4.2f (s %4.2f x c %4.2f)", + score, w, c->weight.GetFloat() ); + } + } + else + { + if ( c->required ) + { + exclude = true; + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "failed (+exclude rule)" ); + } + } + else + { + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "failed" ); + } + } + } + + return score; +} + +float CResponseSystem::ScoreCriteriaAgainstRule( const CriteriaSet& set, ResponseRulePartition::tRuleDict &dict, int irule, bool verbose /*=false*/ ) +{ + Rule * RESTRICT rule = dict[ irule ]; + float score = 0.0f; + + bool bBeingWatched = false; + + // See if we're trying to debug this rule + const char *pszText = rr_debugrule.GetString(); + if ( pszText && pszText[0] && !Q_stricmp( pszText, dict.GetElementName( irule ) ) ) + { + bBeingWatched = true; + } + + if ( !rule->IsEnabled() ) + { + if ( bBeingWatched ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Rule is disabled.\n" ); + } + return 0.0f; + } + + if ( bBeingWatched ) + { + verbose = true; + } + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Scoring rule '%s' (%i)\n{\n", dict.GetElementName( irule ), irule+1 ); + } + + // Iterate set criteria + int count = rule->m_Criteria.Count(); + int i; + for ( i = 0; i < count; i++ ) + { + int icriterion = rule->m_Criteria[ i ]; + + bool exclude = false; + score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, exclude, verbose ); + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, ", score %4.2f\n", score ); + } + + if ( exclude ) + { + score = 0.0f; + break; + } + } + + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "}\n" ); + } + + if ( rule->m_nForceWeight > 0 ) + { // this means override the cumulative weight of criteria and just force the rule's total score, + // assuming it matched at all. + return fsel( score - FLT_MIN, rule->m_nForceWeight, 0 ); + } + else + { + return score; +} +} + +void CResponseSystem::DebugPrint( int depth, const char *fmt, ... ) +{ + int indentchars = 3 * depth; + char *indent = (char *) stackalloc( indentchars + 1); + indent[ indentchars ] = 0; + while ( --indentchars >= 0 ) + { + indent[ indentchars ] = ' '; + } + + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start (argptr, fmt); + Q_vsnprintf (szText, sizeof( szText ), fmt, argptr); + va_end (argptr); + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "%s%s", indent, szText ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::ResetResponseGroups() +{ + int i; + int c = m_Responses.Count(); + for ( i = 0; i < c; i++ ) + { + m_Responses[ i ].Reset(); + } + +#ifdef MAPBASE + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + m_RulePartitions[ idx ].m_bEnabled = true; + } +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::DisableEmptyRules() +{ + if (rr_disableemptyrules.GetBool() == false) + return; + + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + Rule &rule = m_RulePartitions[ idx ]; + + // Set it as disabled in advance + rule.m_bEnabled = false; + + int c2 = rule.m_Responses.Count(); + for (int s = 0; s < c2; s++) + { + if (m_Responses[rule.m_Responses[s]].IsEnabled()) + { + // Re-enable it if there's any valid responses + rule.m_bEnabled = true; + break; + } + } + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Make certain responses unavailable by marking them as depleted +//----------------------------------------------------------------------------- +void CResponseSystem::FakeDepletes( ResponseGroup *g, IResponseFilter *pFilter ) +{ + m_FakedDepletes.RemoveAll(); + + // Fake depletion of unavailable choices + int c = g->group.Count(); + if ( pFilter && g->ShouldCheckRepeats() ) + { + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( r->depletioncount != g->GetDepletionCount() && !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + m_FakedDepletes.AddToTail( i ); + g->MarkResponseUsed( i ); + } + } + } + + // Fake depletion of choices that fail the odds check + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( RandomInt( 1, 100 ) > r->params.odds ) + { + m_FakedDepletes.AddToTail( i ); + g->MarkResponseUsed( i ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Restore responses that were faked as being depleted +//----------------------------------------------------------------------------- +void CResponseSystem::RevertFakedDepletes( ResponseGroup *g ) +{ + for ( int i = 0; i < m_FakedDepletes.Count(); i++ ) + { + g->group[ m_FakedDepletes[ i ] ].depletioncount = 0; + } + m_FakedDepletes.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *g - +// Output : int +//----------------------------------------------------------------------------- +int CResponseSystem::SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ) +{ + int c = g->group.Count(); + if ( !c ) + { + Assert( !"Expecting response group with >= 1 elements" ); + return -1; + } + + FakeDepletes( g, pFilter ); + + if ( !g->HasUndepletedChoices() ) + { + g->ResetDepletionCount(); + + FakeDepletes( g, pFilter ); + + if ( !g->HasUndepletedChoices() ) + return -1; + + // Disable the group if we looped through all the way + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); +#ifdef MAPBASE + DisableEmptyRules(); +#endif + return -1; + } + } + + bool checkrepeats = g->ShouldCheckRepeats(); + int depletioncount = g->GetDepletionCount(); + + float totalweight = 0.0f; + int slot = -1; + + if ( checkrepeats ) + { + int check= -1; + // Snag the first slot right away + if ( g->HasUndepletedFirst( check ) && check != -1 ) + { + slot = check; + } + + if ( slot == -1 && g->HasUndepletedLast( check ) && check != -1 ) + { + // If this is the only undepleted one, use it now + int i; + for ( i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( checkrepeats && + ( r->depletioncount == depletioncount ) ) + { + continue; + } + + if ( r->last ) + { + Assert( i == check ); + continue; + } + + // There's still another undepleted entry + break; + } + + // No more undepleted so use the r->last slot + if ( i >= c ) + { + slot = check; + } + } + } + + if ( slot == -1 ) + { + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &g->group[ i ]; + if ( checkrepeats && + ( r->depletioncount == depletioncount ) ) + { + continue; + } + + // Always skip last entry here since we will deal with it above + if ( checkrepeats && r->last ) + continue; + + int prevSlot = slot; + + if ( !totalweight ) + { + slot = i; + } + + // Always assume very first slot will match + totalweight += r->weight.GetFloat(); + if ( !totalweight || IEngineEmulator::Get()->GetRandomStream()->RandomFloat(0,totalweight) < r->weight.GetFloat() ) + { + slot = i; + } + + if ( !checkrepeats && slot != prevSlot && pFilter && !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + slot = prevSlot; + totalweight -= r->weight.GetFloat(); + } + } + } + + if ( slot != -1 ) + { +#ifdef MAPBASE + // Don't mark responses as used in prospective mode + if (m_bInProspective == false) +#endif + g->MarkResponseUsed( slot ); + } + + // Revert fake depletion of unavailable choices + RevertFakedDepletes( g ); + + return slot; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : searchResult - +// depth - +// *name - +// verbose - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CResponseSystem::ResolveResponse( ResponseSearchResult& searchResult, int depth, const char *name, bool verbose /*= false*/, IResponseFilter *pFilter ) +{ + int responseIndex = m_Responses.Find( name ); + if ( responseIndex == m_Responses.InvalidIndex() ) + return false; + + ResponseGroup *g = &m_Responses[ responseIndex ]; + // Group has been disabled + if ( !g->IsEnabled() ) + return false; + + int c = g->group.Count(); + if ( !c ) + return false; + + int idx = 0; + + if ( g->IsSequential() ) + { + // See if next index is valid + int initialIndex = g->GetCurrentIndex(); + bool bFoundValid = false; + + do + { + idx = g->GetCurrentIndex(); + g->SetCurrentIndex( idx + 1 ); + if ( idx >= c ) + { + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); +#ifdef MAPBASE + DisableEmptyRules(); +#endif + return false; + } + idx = 0; + g->SetCurrentIndex( 0 ); + } + + if ( !pFilter || pFilter->IsValidResponse( g->group[idx].GetType(), g->group[idx].value ) ) + { + bFoundValid = true; + break; + } + + } while ( g->GetCurrentIndex() != initialIndex ); + + if ( !bFoundValid ) + return false; + } + else + { + idx = SelectWeightedResponseFromResponseGroup( g, pFilter ); + if ( idx < 0 ) + return false; + } + + if ( verbose ) + { + DebugPrint( depth, "%s\n", m_Responses.GetElementName( responseIndex ) ); + DebugPrint( depth, "{\n" ); + DescribeResponseGroup( g, idx, depth ); + } + + bool bret = true; + + ParserResponse *result = &g->group[ idx ]; + if ( result->type == RESPONSE_RESPONSE ) + { + // Recurse + bret = ResolveResponse( searchResult, depth + 1, result->value, verbose, pFilter ); + } + else + { + searchResult.action = result; + searchResult.group = g; + } + + if( verbose ) + { + DebugPrint( depth, "}\n" ); + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *group - +// selected - +// depth - +//----------------------------------------------------------------------------- +void CResponseSystem::DescribeResponseGroup( ResponseGroup *group, int selected, int depth ) +{ + int c = group->group.Count(); + + for ( int i = 0; i < c ; i++ ) + { + ParserResponse *r = &group->group[ i ]; + DebugPrint( depth + 1, "%s%20s : %40s %5.3f\n", + i == selected ? "-> " : " ", + CRR_Response::DescribeResponse( r->GetType() ), + r->value, + r->weight.GetFloat() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *rule - +// Output : CResponseSystem::Response +//----------------------------------------------------------------------------- +bool CResponseSystem::GetBestResponse( ResponseSearchResult& searchResult, Rule *rule, bool verbose /*=false*/, IResponseFilter *pFilter ) +{ + int c = rule->m_Responses.Count(); + if ( !c ) + return false; + + int index = IEngineEmulator::Get()->GetRandomStream()->RandomInt( 0, c - 1 ); + int groupIndex = rule->m_Responses[ index ]; + + ResponseGroup *g = &m_Responses[ groupIndex ]; + + // Group has been disabled + if ( !g->IsEnabled() ) + return false; + + int count = g->group.Count(); + if ( !count ) + return false; + + int responseIndex = 0; + + if ( g->IsSequential() ) + { + // See if next index is valid + int initialIndex = g->GetCurrentIndex(); + bool bFoundValid = false; + + do + { + responseIndex = g->GetCurrentIndex(); + g->SetCurrentIndex( responseIndex + 1 ); + if ( responseIndex >= count ) + { + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); +#ifdef MAPBASE + DisableEmptyRules(); +#endif + return false; + } + responseIndex = 0; + g->SetCurrentIndex( 0 ); + } + + if ( !pFilter || pFilter->IsValidResponse( g->group[responseIndex].GetType(), g->group[responseIndex].value ) ) + { + bFoundValid = true; + break; + } + + } while ( g->GetCurrentIndex() != initialIndex ); + + if ( !bFoundValid ) + return false; + } + else + { + responseIndex = SelectWeightedResponseFromResponseGroup( g, pFilter ); + if ( responseIndex < 0 ) + return false; + } + + + ParserResponse *r = &g->group[ responseIndex ]; + + int depth = 0; + + if ( verbose ) + { + DebugPrint( depth, "%s\n", m_Responses.GetElementName( groupIndex ) ); + DebugPrint( depth, "{\n" ); + + DescribeResponseGroup( g, responseIndex, depth ); + } + + bool bret = true; + + if ( r->type == RESPONSE_RESPONSE ) + { + bret = ResolveResponse( searchResult, depth + 1, r->value, verbose, pFilter ); + } + else + { + searchResult.action = r; + searchResult.group = g; + } + + if ( verbose ) + { + DebugPrint( depth, "}\n" ); + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// verbose - +// Output : int +// Warning: If you change this, be sure to also change +// ResponseSystemImplementationCLI::FindAllRulesMatchingCriteria(). +//----------------------------------------------------------------------------- +ResponseRulePartition::tIndex CResponseSystem::FindBestMatchingRule( const CriteriaSet& set, bool verbose, float &scoreOfBestMatchingRule ) +{ + CUtlVector< ResponseRulePartition::tIndex > bestrules(16,4); + float bestscore = 0.001f; + scoreOfBestMatchingRule = 0; + + CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > buckets( 0, 2 ); + m_RulePartitions.GetDictsForCriteria( &buckets, set ); + for ( int b = 0 ; b < buckets.Count() ; ++b ) + { + ResponseRulePartition::tRuleDict *prules = buckets[b]; + int c = prules->Count(); + int i; + for ( i = 0; i < c; i++ ) + { + float score = ScoreCriteriaAgainstRule( set, *prules, i, verbose ); + // Check equals so that we keep track of all matching rules + if ( score >= bestscore ) + { + // Reset bucket + if( score != bestscore ) + { + bestscore = score; + bestrules.RemoveAll(); + } + + // Add to bucket + bestrules.AddToTail( m_RulePartitions.IndexFromDictElem( prules, i ) ); + } + } + } + + int bestCount = bestrules.Count(); + if ( bestCount <= 0 ) + return m_RulePartitions.InvalidIdx(); + + scoreOfBestMatchingRule = bestscore ; + if ( bestCount == 1 ) + { + return bestrules[ 0 ] ; + } + else + { + // Randomly pick one of the tied matching rules + int idx = IEngineEmulator::Get()->GetRandomStream()->RandomInt( 0, bestCount - 1 ); + if ( verbose ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Found %i matching rules, selecting slot %i\n", bestCount, idx ); + } + return bestrules[ idx ] ; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// Output : CRR_Response +//----------------------------------------------------------------------------- +bool CResponseSystem::FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter ) +{ + bool valid = false; + + int iDbgResponse = rr_debugresponses.GetInt(); + bool showRules = ( iDbgResponse >= 2 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ); + bool showResult = ( iDbgResponse >= 1 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ); + + // Look for match. verbose mode used to be at level 2, but disabled because the writers don't actually care for that info. + float scoreOfBestRule; + ResponseRulePartition::tIndex bestRule = FindBestMatchingRule( set, + ( iDbgResponse >= 3 && iDbgResponse < RR_DEBUGRESPONSES_SPECIALCASE ), + scoreOfBestRule ); + + ResponseType_t responseType = RESPONSE_NONE; + ResponseParams rp; + + char ruleName[ 128 ]; + char responseName[ 128 ]; + const char *context; +#ifdef MAPBASE + int contextflags; +#else + bool bcontexttoworld; +#endif + ruleName[ 0 ] = 0; + responseName[ 0 ] = 0; + context = NULL; +#ifdef MAPBASE + contextflags = 0; +#else + bcontexttoworld = false; +#endif + if ( m_RulePartitions.IsValid( bestRule ) ) + { + Rule * RESTRICT r = &m_RulePartitions[ bestRule ]; + + ResponseSearchResult result; + if ( GetBestResponse( result, r, showResult, pFilter ) ) + { + Q_strncpy( responseName, result.action->value, sizeof( responseName ) ); + responseType = result.action->GetType(); + rp = result.action->params; + rp.m_pFollowup = &result.action->m_followup; + } + + Q_strncpy( ruleName, m_RulePartitions.GetElementName( bestRule ), sizeof( ruleName ) ); + + // Disable the rule if it only allows for matching one time + if ( r->IsMatchOnce() ) + { + r->Disable(); + } + context = r->GetContext(); +#ifdef MAPBASE + contextflags = r->GetContextFlags(); + + // Sets the internal indices for the response to call back to later for prospective responses + // (NOTE: Performance not tested; Be wary of turning off the m_bInProspective check!) + if (m_bInProspective) + { + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + if (&m_Responses[i] == result.group) + { + ResponseGroup &group = m_Responses[i]; + for ( int j = 0; j < group.group.Count(); j++) + { + if (&group.group[j] == result.action) + { + response.SetInternalIndices( i, j ); + } + } + } + } + } +#else + bcontexttoworld = r->IsApplyContextToWorld(); +#endif + + response.SetMatchScore(scoreOfBestRule); + valid = true; + } + +#ifdef MAPBASE + response.Init( responseType, responseName, rp, ruleName, context, contextflags ); +#else + response.Init( responseType, responseName, rp, ruleName, context, bcontexttoworld ); +#endif + + if ( showResult ) + { + /* + // clipped -- chet doesn't really want this info + if ( valid ) + { + // Rescore the winner and dump to console + ScoreCriteriaAgainstRule( set, bestRule, true ); + } + */ + + + if ( valid || showRules ) + { + const char *pConceptFilter = rr_debugresponseconcept.GetString(); + // Describe the response, too + if ( V_strlen(pConceptFilter) > 0 && !rr_debugresponseconcept.GetBool() ) + { // filter for only one concept + if ( V_stricmp(pConceptFilter, set.GetValue(set.FindCriterionIndex("concept")) ) == 0 ) + { + response.Describe(&set); + } // else don't print + } + else + { + // maybe we need to filter *out* some concepts + if ( m_DebugExcludeList.IsValidIndex( m_DebugExcludeList.Head() ) ) + { + // we are excluding at least one concept + CRR_Concept test( set.GetValue(set.FindCriterionIndex("concept")) ); + if ( ! m_DebugExcludeList.IsValidIndex( m_DebugExcludeList.Find( test ) ) ) + { // if not found in exclude list, then print + response.Describe(&set); + } + } + else + { + // describe everything + response.Describe(&set); + } + } + } + } + + return valid; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::GetAllResponses( CUtlVector *pResponses ) +{ + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + ResponseGroup &group = m_Responses[i]; + + for ( int j = 0; j < group.group.Count(); j++) + { + ParserResponse &response = group.group[j]; + if ( response.type != RESPONSE_RESPONSE ) + { + /* + CRR_Response *pResponse = new CRR_Response; + pResponse->Init( response.GetType(), response.value, CriteriaSet(), response.params, NULL, NULL, false ); + pResponses->AddToTail(pResponse); + */ + pResponses->Element(pResponses->AddToTail()).Init( response.GetType(), response.value, response.params, NULL, NULL, false ); + } + } + } +} + +#ifdef MAPBASE +void CResponseSystem::MarkResponseAsUsed( short iGroup, short iWithinGroup ) +{ + if (m_Responses.Count() > (unsigned int)iGroup) + { + ResponseGroup &group = m_Responses[iGroup]; + if (group.group.Count() > (int)iWithinGroup) + { + group.MarkResponseUsed( iWithinGroup ); + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Marked response %s (%i) used\n", group.group[iWithinGroup].value, iWithinGroup ); + } + } +} +#endif + +void CResponseSystem::ParseInclude() +{ + char includefile[ 256 ]; + ParseToken(); + +#ifdef MAPBASE + char scriptfile[256]; + GetCurrentScript( scriptfile, sizeof( scriptfile ) ); + + // Gets first path + // (for example, an #include from a file in resource/script/resp will return resource) + size_t len = strlen(scriptfile)-1; + for (size_t i = 0; i < len; i++) + { + if (scriptfile[i] == CORRECT_PATH_SEPARATOR || scriptfile[i] == INCORRECT_PATH_SEPARATOR) + { + len = i; + } + } + Q_strncpy(includefile, scriptfile, len+1); + + if (len+1 != strlen(scriptfile)) + { + Q_strncat( includefile, "/", sizeof( includefile ) ); + Q_strncat( includefile, token, sizeof( includefile ) ); + } + else + includefile[0] = '\0'; + + if (!includefile[0]) + Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); +#else + Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); +#endif + + // check if the file is already included + if ( m_IncludedFiles.Find( includefile ) != NULL ) + { + return; + } + + MEM_ALLOC_CREDIT(); + + // Try and load it + CUtlBuffer buf; + if ( !IEngineEmulator::Get()->GetFilesystem()->ReadFile( includefile, "GAME", buf ) ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Unable to load #included script %s\n", includefile ); + return; + } + + LoadFromBuffer( includefile, (const char *)buf.PeekGet() ); +} + +void CResponseSystem::LoadFromBuffer( const char *scriptfile, const char *buffer ) +{ + COM_TimestampedLog( "CResponseSystem::LoadFromBuffer [%s] - Start", scriptfile ); + m_IncludedFiles.Allocate( scriptfile ); + PushScript( scriptfile, (unsigned char * )buffer ); + + if( rr_dumpresponses.GetBool() ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM,"Reading: %s\n", scriptfile ); + } + + while ( 1 ) + { + ParseToken(); + if ( !token[0] ) + { + break; + } + + unsigned int hash = RR_HASH( token ); + bool bSuccess = Dispatch( token, hash, m_FileDispatch ); + if ( !bSuccess ) + { + int byteoffset = m_ScriptStack[ 0 ].currenttoken - (const char *)m_ScriptStack[ 0 ].buffer; + + Error( "CResponseSystem::LoadFromBuffer: Unknown entry type '%s', expecting 'response', 'criterion', 'enumeration' or 'rules' in file %s(offset:%i)\n", + token, scriptfile, byteoffset ); + break; + } + } + + if ( m_ScriptStack.Count() == 1 ) + { + char cur[ 256 ]; + GetCurrentScript( cur, sizeof( cur ) ); + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "CResponseSystem: %s (%i rules, %i criteria, and %i responses)\n", + cur, m_RulePartitions.Count(), m_Criteria.Count(), m_Responses.Count() ); + + if( rr_dumpresponses.GetBool() ) + { + DumpRules(); + } + } + + PopScript(); + COM_TimestampedLog( "CResponseSystem::LoadFromBuffer [%s] - Finish", scriptfile ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::LoadRuleSet( const char *basescript ) +{ + float flStart = Plat_FloatTime(); + int length = 0; + unsigned char *buffer = (unsigned char *)IEngineEmulator::Get()->LoadFileForMe( basescript, &length ); + if ( length <= 0 || !buffer ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "CResponseSystem: failed to load %s\n", basescript ); + return; + } + + m_IncludedFiles.FreeAll(); + LoadFromBuffer( basescript, (const char *)buffer ); + + IEngineEmulator::Get()->FreeFile( buffer ); + + Assert( m_ScriptStack.Count() == 0 ); + float flEnd = Plat_FloatTime(); + COM_TimestampedLog( "CResponseSystem::LoadRuleSet took %f msec", 1000.0f * ( flEnd - flStart ) ); +} + +inline ResponseType_t ComputeResponseType( const char *s ) +{ + switch ( s[ 0 ] ) + { + default: + break; + case 's': + switch ( s[ 1 ] ) + { + default: + break; + case 'c': + return RESPONSE_SCENE; + case 'e': + return RESPONSE_SENTENCE; + case 'p': + return RESPONSE_SPEAK; + } + break; + case 'r': + return RESPONSE_RESPONSE; + case 'p': + return RESPONSE_PRINT; + case 'e': + return RESPONSE_ENTITYIO; +#ifdef MAPBASE + case 'v': + if (*(s + 7) == '_') + return RESPONSE_VSCRIPT_FILE; + else + return RESPONSE_VSCRIPT; +#endif + } + + return RESPONSE_NONE; +} + +void CResponseSystem::ParseResponse_Weight( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + newResponse.weight.SetFloat( (float)atof( token ) ); +} + +void CResponseSystem::ParseResponse_PreDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYBEFORESPEAK; + rp->predelay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_NoDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = 0; + rp->delay.range = 0; +} + +void CResponseSystem::ParseResponse_DefaultDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = AIS_DEF_MIN_DELAY; + rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); +} + +void CResponseSystem::ParseResponse_Delay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_SpeakOnce( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_SPEAKONCE; +} + +void CResponseSystem::ParseResponse_NoScene( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE; +} + +void CResponseSystem::ParseResponse_StopOnNonIdle( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; +} + +void CResponseSystem::ParseResponse_Odds( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_ODDS; + rp->odds = clamp( atoi( token ), 0, 100 ); +} + +void CResponseSystem::ParseResponse_RespeakDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY; + rp->respeakdelay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_WeaponDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_WEAPONDELAY; + rp->weapondelay.FromInterval( ReadInterval( token ) ); +} + +void CResponseSystem::ParseResponse_Soundlevel( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + ParseToken(); + rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL; + rp->soundlevel = (soundlevel_t)TextToSoundLevel( token ); +} + +void CResponseSystem::ParseResponse_DisplayFirst( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + newResponse.first = true; + group.m_bHasFirst = true; +} + +void CResponseSystem::ParseResponse_DisplayLast( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + newResponse.last = true; + group.m_bHasLast= true; +} + +void CResponseSystem::ParseResponse_Fire( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + // get target name + bool bSuc = ParseToken(); + if (!bSuc) + { + ResponseWarning( "FIRE token in response needs exactly three parameters." ); + return; + } + newResponse.m_followup.followup_entityiotarget = ResponseCopyString(token); + + bSuc = ParseToken(); + if (!bSuc) + { + ResponseWarning( "FIRE token in response needs exactly three parameters." ); + return; + } + newResponse.m_followup.followup_entityioinput = ResponseCopyString(token); + + bSuc = ParseToken(); + if (!bSuc) + { + ResponseWarning( "FIRE token in response needs exactly three parameters." ); + return; + } + newResponse.m_followup.followup_entityiodelay = atof( token ); + /* + m_followup.followup_entityioinput = ResponseCopyString(src.m_followup.followup_entityioinput); + m_followup.followup_entityiotarget = ResponseCopyString(src.m_followup.followup_entityiotarget); + */ +} + +void CResponseSystem::ParseResponse_Then( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + // eg, "subject TALK_ANSWER saidunplant:1 3" + bool bSuc = ParseToken(); + if (!bSuc) + { + AssertMsg(false, "THEN token in response lacked any further info.\n"); + ResponseWarning( "THEN token in response lacked any further info.\n" ); + return; + } + + newResponse.m_followup.followup_target = ResponseCopyString(token); + + bSuc = ParseToken(); // get another token + if (!bSuc) + { + AssertMsg1(false, "THEN token in response had a target '%s', but lacked any further info.\n", newResponse.m_followup.followup_target ); + ResponseWarning( "THEN token in response had a target '%s', but lacked any further info.\n", newResponse.m_followup.followup_target ); + return; + } + + newResponse.m_followup.followup_concept = ResponseCopyString( token ); + + + // Okay, this is totally asinine. + // Because the ParseToken() function will split foo:bar into three tokens + // (which is reasonable), but we have no safe way to parse the file otherwise + // because it's all behind an engine interface, it's necessary to parse all + // the tokens to the end of the line and catenate them, except for the last one + // which is the delay. That's crap. + bSuc = ParseToken(); + if (!bSuc) + { + AssertMsg(false, "THEN token in response lacked contexts.\n"); + ResponseWarning( "THEN token in response lacked contexts.\n" ); + return; + } + + // okay, as long as there is at least one more token, catenate the ones we + // see onto a temporary buffer. When we're down to the last token, that is + // the delay. + char buf[4096]; + buf[0] = '\0'; + while ( TokenWaiting() ) + { + Q_strncat( buf, token, 4096 ); + bSuc = ParseToken(); + AssertMsg(bSuc, "Token parsing mysteriously failed."); + } + + // down here, token is the last token, and buf is everything up to there. + newResponse.m_followup.followup_contexts = ResponseCopyString( buf ); + + newResponse.m_followup.followup_delay = atof( token ); +} + +void CResponseSystem::ParseOneResponse( const char *responseGroupName, ResponseGroup& group, ResponseParams *defaultParams ) +{ + ParserResponse &newResponse = group.group[ group.group.AddToTail() ]; + newResponse.weight.SetFloat( 1.0f ); + // inherit from group if appropriate + if (defaultParams) + { + newResponse.params = *defaultParams; + } + + ResponseParams *rp = &newResponse.params; + + newResponse.type = ComputeResponseType( token ); + if ( RESPONSE_NONE == newResponse.type ) +{ + ResponseWarning( "response entry '%s' with unknown response type '%s'\n", responseGroupName, token ); + return; +} + +#ifdef MAPBASE + // HACKHACK: Some response system usage in the pre-Alien Swarm system require response names to preserve casing or even have escaped quotes. + ParseTokenIntact(); +#else + ParseToken(); +#endif + newResponse.value = ResponseCopyString( token ); + + while ( TokenWaiting() ) + { + ParseToken(); + + unsigned int hash = RR_HASH( token ); + if ( DispatchParseResponse( token, hash, m_ResponseDispatch, newResponse, group, rp ) ) + { + continue; + } + + ResponseWarning( "response entry '%s' with unknown command '%s'\n", responseGroupName, token ); + } + +} + +void CResponseSystem::ParseResponseGroup_Start( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + if ( !Q_stricmp( token, "permitrepeats" ) ) + { + newGroup.m_bDepleteBeforeRepeat = false; + continue; + } + else if ( !Q_stricmp( token, "sequential" ) ) + { + newGroup.SetSequential( true ); + continue; + } + else if ( !Q_stricmp( token, "norepeat" ) ) + { + newGroup.SetNoRepeat( true ); + continue; + } + + ParseOneResponse( responseGroupName, newGroup, &groupResponseParams ); + } + } + +void CResponseSystem::ParseResponseGroup_PreDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYBEFORESPEAK; + groupResponseParams.predelay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_NoDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + groupResponseParams.delay.start = 0; + groupResponseParams.delay.range = 0; + } + +void CResponseSystem::ParseResponseGroup_DefaultDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + groupResponseParams.delay.start = AIS_DEF_MIN_DELAY; + groupResponseParams.delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); + } + +void CResponseSystem::ParseResponseGroup_Delay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + groupResponseParams.delay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_SpeakOnce( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_SPEAKONCE; + } + +void CResponseSystem::ParseResponseGroup_NoScene( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_DONT_USE_SCENE; + } + +void CResponseSystem::ParseResponseGroup_StopOnNonIdle( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + groupResponseParams.flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; + } + +void CResponseSystem::ParseResponseGroup_Odds( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_ODDS; + groupResponseParams.odds = clamp( atoi( token ), 0, 100 ); + } + +void CResponseSystem::ParseResponseGroup_RespeakDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_RESPEAKDELAY; + groupResponseParams.respeakdelay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_WeaponDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_WEAPONDELAY; + groupResponseParams.weapondelay.FromInterval( ReadInterval( token ) ); + } + +void CResponseSystem::ParseResponseGroup_Soundlevel( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ) + { + ParseToken(); + groupResponseParams.flags |= AI_ResponseParams::RG_SOUNDLEVEL; + groupResponseParams.soundlevel = (soundlevel_t)TextToSoundLevel( token ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::ParseResponse( void ) +{ + AI_ResponseParams groupResponseParams; // default response parameters inherited from single line format for group + + // Should have groupname at start + ParseToken(); + char responseGroupName[ 128 ]; + Q_strncpy( responseGroupName, token, sizeof( responseGroupName ) ); + + int slot = m_Responses.Insert( responseGroupName ); + ResponseGroup &newGroup = m_Responses[ slot ]; + + while ( 1 ) + { +#ifdef MAPBASE + if ( !ParseToken() || !Q_stricmp( token, "}" ) ) + { + break; + } +#else + ParseToken(); +#endif + + unsigned int hash = RR_HASH( token ); + + // Oops, part of next definition + if( IsRootCommand( hash ) ) + { + Unget(); + break; + } + + if ( DispatchParseResponseGroup( token, hash, m_ResponseGroupDispatch, responseGroupName, newGroup, groupResponseParams ) ) + { + continue; + } + + ParseOneResponse( responseGroupName, newGroup, &groupResponseParams ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criterion - +//----------------------------------------------------------------------------- +int CResponseSystem::ParseOneCriterion( const char *criterionName ) +{ + char key[ 128 ]; + char value[ 128 ]; + + Criteria *pNewCriterion = NULL; + + int idx; +#ifdef MAPBASE + short existing = m_Criteria.Find( criterionName ); + if ( existing != m_Criteria.InvalidIndex() ) + { + //ResponseWarning( "Additional definition for criteria '%s', overwriting\n", criterionName ); + m_Criteria[existing] = Criteria(); + m_Criteria.SetElementName(existing, criterionName); + idx = existing; + pNewCriterion = &m_Criteria[ idx ]; + } +#else + if ( m_Criteria.Find( criterionName ) != m_Criteria.InvalidIndex() ) + { + static Criteria dummy; + pNewCriterion = &dummy; + + ResponseWarning( "Multiple definitions for criteria '%s' [%d]\n", criterionName, RR_HASH( criterionName ) ); + idx = m_Criteria.InvalidIndex(); + } +#endif + else + { + idx = m_Criteria.Insert( criterionName ); + pNewCriterion = &m_Criteria[ idx ]; + } + + bool gotbody = false; + + while ( TokenWaiting() || !gotbody ) + { +#ifdef MAPBASE + if ( !ParseToken() ) + { + break; + } +#else + ParseToken(); +#endif + + // Oops, part of next definition + if( IsRootCommand() ) + { + Unget(); + break; + } + + if ( !Q_stricmp( token, "{" ) ) + { + gotbody = true; + + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + // Look up subcriteria index + int idx = m_Criteria.Find( token ); + if ( idx != m_Criteria.InvalidIndex() ) + { + pNewCriterion->subcriteria.AddToTail( idx ); + } + else + { + ResponseWarning( "Skipping unrecongized subcriterion '%s' in '%s'\n", token, criterionName ); + } + } + continue; + } + else if ( !Q_stricmp( token, "required" ) ) + { + pNewCriterion->required = true; + } + else if ( !Q_stricmp( token, "weight" ) ) + { + ParseToken(); + pNewCriterion->weight.SetFloat( (float)atof( token ) ); + } + else + { + Assert( pNewCriterion->subcriteria.Count() == 0 ); + + // Assume it's the math info for a non-subcriteria resposne + Q_strncpy( key, token, sizeof( key ) ); + ParseToken(); + Q_strncpy( value, token, sizeof( value ) ); + + V_strlower( key ); + pNewCriterion->nameSym = CriteriaSet::ComputeCriteriaSymbol( key ); + pNewCriterion->value = ResponseCopyString( value ); + + gotbody = true; + } + } + + if ( !pNewCriterion->IsSubCriteriaType() ) + { + ComputeMatcher( pNewCriterion, pNewCriterion->matcher ); + } + + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseCriterion( void ) +{ + // Should have groupname at start + char criterionName[ 128 ]; + ParseToken(); + Q_strncpy( criterionName, token, sizeof( criterionName ) ); + + ParseOneCriterion( criterionName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseEnumeration( void ) +{ + char enumerationName[ 128 ]; + ParseToken(); + Q_strncpy( enumerationName, token, sizeof( enumerationName ) ); + + ParseToken(); + if ( Q_stricmp( token, "{" ) ) + { + ResponseWarning( "Expecting '{' in enumeration '%s', got '%s'\n", enumerationName, token ); + return; + } + + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + if ( Q_strlen( token ) <= 0 ) + { + ResponseWarning( "Expecting more tokens in enumeration '%s'\n", enumerationName ); + break; + } + + char key[ 128 ]; + + Q_strncpy( key, token, sizeof( key ) ); + ParseToken(); + float value = (float)atof( token ); + + char sz[ 128 ]; + Q_snprintf( sz, sizeof( sz ), "[%s::%s]", enumerationName, key ); + Q_strlower( sz ); + + Enumeration newEnum; + newEnum.value = value; + + if ( m_Enumerations.Find( sz ) == m_Enumerations.InvalidIndex() ) + { + m_Enumerations.Insert( sz, newEnum ); + } + /* + else + { + ResponseWarning( "Ignoring duplication enumeration '%s'\n", sz ); + } + */ + } +} + +void CResponseSystem::ParseRule_MatchOnce( Rule &newRule ) + { + newRule.m_bMatchOnce = true; + } + +#ifdef MAPBASE +void CResponseSystem::ParseRule_ApplyContextToWorld( Rule &newRule ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_WORLD; + } + +void CResponseSystem::ParseRule_ApplyContextToSquad( Rule &newRule ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_SQUAD; + } + +void CResponseSystem::ParseRule_ApplyContextToEnemy( Rule &newRule ) + { + newRule.m_iContextFlags |= APPLYCONTEXT_ENEMY; + } +#else +void CResponseSystem::ParseRule_ApplyContextToWorld( Rule &newRule ) + { + newRule.m_bApplyContextToWorld = true; + } +#endif + +void CResponseSystem::ParseRule_ApplyContext( Rule &newRule ) + { + ParseToken(); + if ( newRule.GetContext() == NULL ) + { + newRule.SetContext( token ); + } + else + { + CFmtStrN<1024> newContext( "%s,%s", newRule.GetContext(), token ); + newRule.SetContext( newContext ); + } + } + +void CResponseSystem::ParseRule_Response( Rule &newRule ) + { + // Read them until we run out. + while ( TokenWaiting() ) + { + ParseToken(); + int idx = m_Responses.Find( token ); + if ( idx != m_Responses.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + newRule.m_Responses.AddToTail( idx ); + } + else + { + m_bParseRuleValid = false; + ResponseWarning( "No such response '%s' for rule '%s'\n", token, m_pParseRuleName ); + } + } +} + +/* +void CResponseSystem::ParseRule_ForceWeight( Rule &newRule ) +{ + ParseToken(); + if ( token[0] == 0 ) + { + // no token followed forceweight? + ResponseWarning( "Forceweight token in rule '%s' did not specify a numerical weight! Ignoring.\n", m_pParseRuleName ); + } + else + { + newRule.m_nForceWeight = atoi(token); + if ( newRule.m_nForceWeight == 0 ) + { + ResponseWarning( "Rule '%s' had forceweight '%s', which doesn't work out to a nonzero number. Ignoring.\n", + m_pParseRuleName, token ); + } + } + } +*/ + +void CResponseSystem::ParseRule_Criteria( Rule &newRule ) + { + // Read them until we run out. + while ( TokenWaiting() ) + { + ParseToken(); + + int idx = m_Criteria.Find( token ); + if ( idx != m_Criteria.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + newRule.m_Criteria.AddToTail( idx ); + } + else + { + m_bParseRuleValid = false; + ResponseWarning( "No such criterion '%s' for rule '%s'\n", token, m_pParseRuleName ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseRule( void ) +{ + static int instancedCriteria = 0; + + char ruleName[ 128 ]; + ParseToken(); + Q_strncpy( ruleName, token, sizeof( ruleName ) ); + + ParseToken(); + if ( Q_stricmp( token, "{" ) ) + { + ResponseWarning( "Expecting '{' in rule '%s', got '%s'\n", ruleName, token ); + return; + } + + // entries are "criteria", "response" or an in-line criteria to instance + Rule *newRule = new Rule; + + char sz[ 128 ]; + + m_bParseRuleValid = true; + m_pParseRuleName = ruleName; + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + { + break; + } + + if ( Q_strlen( token ) <= 0 ) + { + ResponseWarning( "Expecting more tokens in rule '%s'\n", ruleName ); + break; + } + + unsigned int hash = RR_HASH( token ); + if ( DispatchParseRule( token, hash, m_RuleDispatch, *newRule ) ) + continue; + + // It's an inline criteria, generate a name and parse it in + Q_snprintf( sz, sizeof( sz ), "[%s%03i]", ruleName, ++instancedCriteria ); + Unget(); + int idx = ParseOneCriterion( sz ); + if ( idx != m_Criteria.InvalidIndex() ) + { + newRule->m_Criteria.AddToTail( idx ); + } + } + + if ( m_bParseRuleValid ) + { + m_RulePartitions.GetDictForRule( this, newRule ).Insert( ruleName, newRule ); + } + else + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Discarded rule %s\n", ruleName ); + delete newRule; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CResponseSystem::GetCurrentToken() const +{ + if ( m_ScriptStack.Count() <= 0 ) + return -1; + + return m_ScriptStack[ 0 ].tokencount; +} + + +void CResponseSystem::ResponseWarning( const char *fmt, ... ) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, fmt); + Q_vsnprintf(string, sizeof(string), fmt,argptr); + va_end (argptr); + + char cur[ 256 ]; + GetCurrentScript( cur, sizeof( cur ) ); + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "%s(token %i) : %s", cur, GetCurrentToken(), string ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyCriteriaFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ) +{ + // Add criteria from this rule to global list in custom response system. + int nCriteriaCount = pSrcRule->m_Criteria.Count(); + for ( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria ) + { + int iSrcIndex = pSrcRule->m_Criteria[iCriteria]; + Criteria *pSrcCriteria = &m_Criteria[iSrcIndex]; + if ( pSrcCriteria ) + { + int iIndex = pCustomSystem->m_Criteria.Find( m_Criteria.GetElementName( iSrcIndex ) ); + if ( iIndex != pCustomSystem->m_Criteria.InvalidIndex() ) + { + pDstRule->m_Criteria.AddToTail( iIndex ); + continue; + } + + // Add the criteria. + Criteria dstCriteria; + + dstCriteria.nameSym = pSrcCriteria->nameSym ; + dstCriteria.value = ResponseCopyString( pSrcCriteria->value ); + dstCriteria.weight = pSrcCriteria->weight; + dstCriteria.required = pSrcCriteria->required; + dstCriteria.matcher = pSrcCriteria->matcher; + + int nSubCriteriaCount = pSrcCriteria->subcriteria.Count(); + for ( int iSubCriteria = 0; iSubCriteria < nSubCriteriaCount; ++iSubCriteria ) + { + int iSrcSubIndex = pSrcCriteria->subcriteria[iSubCriteria]; + Criteria *pSrcSubCriteria = &m_Criteria[iSrcSubIndex]; + if ( pSrcCriteria ) + { + int iSubIndex = pCustomSystem->m_Criteria.Find( pSrcSubCriteria->value ); + if ( iSubIndex != pCustomSystem->m_Criteria.InvalidIndex() ) + continue; + + // Add the criteria. + Criteria dstSubCriteria; + + dstSubCriteria.nameSym = pSrcSubCriteria->nameSym ; + dstSubCriteria.value = ResponseCopyString( pSrcSubCriteria->value ); + dstSubCriteria.weight = pSrcSubCriteria->weight; + dstSubCriteria.required = pSrcSubCriteria->required; + dstSubCriteria.matcher = pSrcSubCriteria->matcher; + + int iSubInsertIndex = pCustomSystem->m_Criteria.Insert( pSrcSubCriteria->value, dstSubCriteria ); + dstCriteria.subcriteria.AddToTail( iSubInsertIndex ); + } + } + + int iInsertIndex = pCustomSystem->m_Criteria.Insert( m_Criteria.GetElementName( iSrcIndex ), dstCriteria ); + pDstRule->m_Criteria.AddToTail( iInsertIndex ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyResponsesFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ) +{ + // Add responses from this rule to global list in custom response system. + int nResponseGroupCount = pSrcRule->m_Responses.Count(); + for ( int iResponseGroup = 0; iResponseGroup < nResponseGroupCount; ++iResponseGroup ) + { + int iSrcResponseGroup = pSrcRule->m_Responses[iResponseGroup]; + ResponseGroup *pSrcResponseGroup = &m_Responses[iSrcResponseGroup]; + if ( pSrcResponseGroup ) + { + // Add response group. + ResponseGroup dstResponseGroup; + + dstResponseGroup.m_bDepleteBeforeRepeat = pSrcResponseGroup->m_bDepleteBeforeRepeat; + dstResponseGroup.m_nDepletionCount = pSrcResponseGroup->m_nDepletionCount; + dstResponseGroup.m_bHasFirst = pSrcResponseGroup->m_bHasFirst; + dstResponseGroup.m_bHasLast = pSrcResponseGroup->m_bHasLast; + dstResponseGroup.m_bSequential = pSrcResponseGroup->m_bSequential; + dstResponseGroup.m_bNoRepeat = pSrcResponseGroup->m_bNoRepeat; + dstResponseGroup.m_bEnabled = pSrcResponseGroup->m_bEnabled; + dstResponseGroup.m_nCurrentIndex = pSrcResponseGroup->m_nCurrentIndex; + + int nSrcResponseCount = pSrcResponseGroup->group.Count(); + for ( int iResponse = 0; iResponse < nSrcResponseCount; ++iResponse ) + { + ParserResponse *pSrcResponse = &pSrcResponseGroup->group[iResponse]; + if ( pSrcResponse ) + { + // Add Response + ParserResponse dstResponse; + + dstResponse.weight = pSrcResponse->weight; + dstResponse.type = pSrcResponse->type; + dstResponse.value = ResponseCopyString( pSrcResponse->value ); + dstResponse.depletioncount = pSrcResponse->depletioncount; + dstResponse.first = pSrcResponse->first; + dstResponse.last = pSrcResponse->last; + + dstResponseGroup.group.AddToTail( dstResponse ); + } + } + + int iInsertIndex = pCustomSystem->m_Responses.Insert( m_Responses.GetElementName( iSrcResponseGroup ), dstResponseGroup ); + pDstRule->m_Responses.AddToTail( iInsertIndex ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyEnumerationsFrom( CResponseSystem *pCustomSystem ) +{ + int nEnumerationCount = m_Enumerations.Count(); + for ( int iEnumeration = 0; iEnumeration < nEnumerationCount; ++iEnumeration ) + { + Enumeration *pSrcEnumeration = &m_Enumerations[iEnumeration]; + if ( pSrcEnumeration ) + { + Enumeration dstEnumeration; + dstEnumeration.value = pSrcEnumeration->value; + pCustomSystem->m_Enumerations.Insert( m_Enumerations.GetElementName( iEnumeration ), dstEnumeration ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::CopyRuleFrom( Rule *pSrcRule, ResponseRulePartition::tIndex iRule, CResponseSystem *pCustomSystem ) +{ + // Verify data. + Assert( pSrcRule ); + Assert( pCustomSystem ); + if ( !pSrcRule || !pCustomSystem ) + return; + + // New rule + Rule *dstRule = new Rule; + + dstRule->SetContext( pSrcRule->GetContext() ); + dstRule->m_bMatchOnce = pSrcRule->m_bMatchOnce; + dstRule->m_bEnabled = pSrcRule->m_bEnabled; +#ifdef MAPBASE + dstRule->m_iContextFlags = pSrcRule->m_iContextFlags; +#else + dstRule->m_bApplyContextToWorld = pSrcRule->m_bApplyContextToWorld; +#endif + + // Copy off criteria. + CopyCriteriaFrom( pSrcRule, dstRule, pCustomSystem ); + + // Copy off responses. + CopyResponsesFrom( pSrcRule, dstRule, pCustomSystem ); + + // Copy off enumerations - Don't think we use these. + // CopyEnumerationsFrom( pCustomSystem ); + + // Add rule. + pCustomSystem->m_RulePartitions.GetDictForRule( this, dstRule ).Insert( m_RulePartitions.GetElementName( iRule ), dstRule ); +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::DumpRules() +{ + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%s\n", m_RulePartitions.GetElementName( idx ) ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::DumpDictionary( const char *pszName ) +{ + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\nDictionary: %s\n", pszName ); + + // int nRuleCount = m_Rules.Count(); + // for ( int iRule = 0; iRule < nRuleCount; ++iRule ) + for ( ResponseRulePartition::tIndex idx = m_RulePartitions.First() ; + m_RulePartitions.IsValid(idx) ; + idx = m_RulePartitions.Next(idx) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Rule %d/%d: %s\n", m_RulePartitions.BucketFromIdx( idx ), m_RulePartitions.PartFromIdx( idx ), m_RulePartitions.GetElementName( idx ) ); + + Rule *pRule = &m_RulePartitions[idx]; + + int nCriteriaCount = pRule->m_Criteria.Count(); + for( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria ) + { + int iRuleCriteria = pRule->m_Criteria[iCriteria]; + Criteria *pCriteria = &m_Criteria[iRuleCriteria]; + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Criteria %d: %s %s\n", iCriteria, CriteriaSet::SymbolToStr( pCriteria->nameSym ), pCriteria->value ); + } + + int nResponseGroupCount = pRule->m_Responses.Count(); + for ( int iResponseGroup = 0; iResponseGroup < nResponseGroupCount; ++iResponseGroup ) + { + int iRuleResponse = pRule->m_Responses[iResponseGroup]; + ResponseGroup *pResponseGroup = &m_Responses[iRuleResponse]; + + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " ResponseGroup %d: %s\n", iResponseGroup, m_Responses.GetElementName( iRuleResponse ) ); + + int nResponseCount = pResponseGroup->group.Count(); + for ( int iResponse = 0; iResponse < nResponseCount; ++iResponse ) + { + ParserResponse *pResponse = &pResponseGroup->group[iResponse]; + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Response %d: %s\n", iResponse, pResponse->value ); + } + } + } +} + +void CResponseSystem::BuildDispatchTables() +{ + m_RootCommandHashes.Insert( RR_HASH( "#include" ) ); + m_RootCommandHashes.Insert( RR_HASH( "response" ) ); + m_RootCommandHashes.Insert( RR_HASH( "enumeration" ) ); + m_RootCommandHashes.Insert( RR_HASH( "criterion" ) ); + m_RootCommandHashes.Insert( RR_HASH( "criteria" ) ); + m_RootCommandHashes.Insert( RR_HASH( "rule" ) ); + + m_FileDispatch.Insert( RR_HASH( "#include" ), &CResponseSystem::ParseInclude ); + m_FileDispatch.Insert( RR_HASH( "response" ), &CResponseSystem::ParseResponse ); + m_FileDispatch.Insert( RR_HASH( "criterion" ), &CResponseSystem::ParseCriterion ); + m_FileDispatch.Insert( RR_HASH( "criteria" ), &CResponseSystem::ParseCriterion ); + m_FileDispatch.Insert( RR_HASH( "rule" ), &CResponseSystem::ParseRule ); + m_FileDispatch.Insert( RR_HASH( "enumeration" ), &CResponseSystem::ParseEnumeration ); + + m_RuleDispatch.Insert( RR_HASH( "matchonce" ), &CResponseSystem::ParseRule_MatchOnce ); + m_RuleDispatch.Insert( RR_HASH( "applycontexttoworld" ), &CResponseSystem::ParseRule_ApplyContextToWorld ); +#ifdef MAPBASE + m_RuleDispatch.Insert( RR_HASH( "applycontexttosquad" ), &CResponseSystem::ParseRule_ApplyContextToSquad ); + m_RuleDispatch.Insert( RR_HASH( "applycontexttoenemy" ), &CResponseSystem::ParseRule_ApplyContextToEnemy ); +#endif + m_RuleDispatch.Insert( RR_HASH( "applycontext" ), &CResponseSystem::ParseRule_ApplyContext ); + m_RuleDispatch.Insert( RR_HASH( "response" ), &CResponseSystem::ParseRule_Response ); +// m_RuleDispatch.Insert( RR_HASH( "forceweight" ), &CResponseSystem::ParseRule_ForceWeight ); + m_RuleDispatch.Insert( RR_HASH( "criteria" ), &CResponseSystem::ParseRule_Criteria ); + m_RuleDispatch.Insert( RR_HASH( "criterion" ), &CResponseSystem::ParseRule_Criteria ); + + + m_ResponseDispatch.Insert( RR_HASH( "weight" ), &CResponseSystem::ParseResponse_Weight ); + m_ResponseDispatch.Insert( RR_HASH( "predelay" ), &CResponseSystem::ParseResponse_PreDelay ); + m_ResponseDispatch.Insert( RR_HASH( "nodelay" ), &CResponseSystem::ParseResponse_NoDelay ); + m_ResponseDispatch.Insert( RR_HASH( "defaultdelay" ), &CResponseSystem::ParseResponse_DefaultDelay ); + m_ResponseDispatch.Insert( RR_HASH( "delay" ), &CResponseSystem::ParseResponse_Delay ); + m_ResponseDispatch.Insert( RR_HASH( "speakonce" ), &CResponseSystem::ParseResponse_SpeakOnce ); + m_ResponseDispatch.Insert( RR_HASH( "noscene" ), &CResponseSystem::ParseResponse_NoScene ); + m_ResponseDispatch.Insert( RR_HASH( "stop_on_nonidle" ), &CResponseSystem::ParseResponse_StopOnNonIdle ); + m_ResponseDispatch.Insert( RR_HASH( "odds" ), &CResponseSystem::ParseResponse_Odds ); + m_ResponseDispatch.Insert( RR_HASH( "respeakdelay" ), &CResponseSystem::ParseResponse_RespeakDelay ); + m_ResponseDispatch.Insert( RR_HASH( "weapondelay" ), &CResponseSystem::ParseResponse_WeaponDelay ); + m_ResponseDispatch.Insert( RR_HASH( "soundlevel" ), &CResponseSystem::ParseResponse_Soundlevel ); + m_ResponseDispatch.Insert( RR_HASH( "displayfirst" ), &CResponseSystem::ParseResponse_DisplayFirst ); + m_ResponseDispatch.Insert( RR_HASH( "displaylast" ), &CResponseSystem::ParseResponse_DisplayLast ); + m_ResponseDispatch.Insert( RR_HASH( "fire" ), &CResponseSystem::ParseResponse_Fire ); + m_ResponseDispatch.Insert( RR_HASH( "then" ), &CResponseSystem::ParseResponse_Then ); + + m_ResponseGroupDispatch.Insert( RR_HASH( "{" ), &CResponseSystem::ParseResponseGroup_Start ); + m_ResponseGroupDispatch.Insert( RR_HASH( "predelay" ), &CResponseSystem::ParseResponseGroup_PreDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "nodelay" ), &CResponseSystem::ParseResponseGroup_NoDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "defaultdelay" ), &CResponseSystem::ParseResponseGroup_DefaultDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "delay" ), &CResponseSystem::ParseResponseGroup_Delay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "speakonce" ), &CResponseSystem::ParseResponseGroup_SpeakOnce ); + m_ResponseGroupDispatch.Insert( RR_HASH( "noscene" ), &CResponseSystem::ParseResponseGroup_NoScene ); + m_ResponseGroupDispatch.Insert( RR_HASH( "stop_on_nonidle" ), &CResponseSystem::ParseResponseGroup_StopOnNonIdle ); + m_ResponseGroupDispatch.Insert( RR_HASH( "odds" ), &CResponseSystem::ParseResponseGroup_Odds ); + m_ResponseGroupDispatch.Insert( RR_HASH( "respeakdelay" ), &CResponseSystem::ParseResponseGroup_RespeakDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "weapondelay" ), &CResponseSystem::ParseResponseGroup_WeaponDelay ); + m_ResponseGroupDispatch.Insert( RR_HASH( "soundlevel" ), &CResponseSystem::ParseResponseGroup_Soundlevel ); +} + +bool CResponseSystem::Dispatch( char const *pToken, unsigned int uiHash, CResponseSystem::DispatchMap_t &rMap ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnResponseDispatch dispatch = rMap[ slot ]; + (this->*dispatch)(); + return true; + } + + return false; +} + +bool CResponseSystem::DispatchParseRule( char const *pToken, unsigned int uiHash, ParseRuleDispatchMap_t &rMap, Rule &newRule ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnParseRuleDispatch dispatch = rMap[ slot ]; + (this->*dispatch)( newRule ); + return true; + } + + return false; +} + +bool CResponseSystem::DispatchParseResponse( char const *pToken, unsigned int uiHash, ParseResponseDispatchMap_t &rMap, ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnParseResponseDispatch dispatch = rMap[ slot ]; + (this->*dispatch)( newResponse, group, rp ); + return true; + } + + return false; +} + +bool CResponseSystem::DispatchParseResponseGroup( char const *pToken, unsigned int uiHash, ParseResponseGroupDispatchMap_t &rMap, char const *responseGroupName, ResponseGroup& newGroup, AI_ResponseParams &groupResponseParams ) +{ + int slot = rMap.Find( uiHash ); + if ( slot != rMap.InvalidIndex() ) + { + CResponseSystem::pfnParseResponseGroupDispatch dispatch = rMap[ slot ]; + (this->*dispatch)( responseGroupName, newGroup, groupResponseParams ); + return true; + } + + return false; +} + +unsigned int ResponseRulePartition::GetBucketForSpeakerAndConcept( const char *pszSpeaker, const char *pszConcept, const char *pszSubject ) +{ + // make sure is a power of two + COMPILE_TIME_ASSERT( ( N_RESPONSE_PARTITIONS & ( N_RESPONSE_PARTITIONS - 1 ) ) == 0 ); + + // hash together the speaker and concept strings, and mask off by the bucket mask + unsigned hashSpeaker = 0; // pszSpeaker ? HashStringCaseless( pszSpeaker ) : 0; + unsigned hashConcept = pszConcept ? HashStringCaseless( pszConcept ) : 0; + unsigned hashSubject = pszSubject ? HashStringCaseless( pszSubject ) : 0; + unsigned hashBrowns = ( ( hashSubject >> 3 ) ^ (hashSpeaker >> 1) ^ hashConcept ) & ( N_RESPONSE_PARTITIONS - 1 ); + return hashBrowns; +} + +const char *Rule::GetValueForRuleCriterionByName( CResponseSystem * RESTRICT pSystem, const CUtlSymbol &pCritNameSym ) +{ + const char * retval = NULL; + // for each rule criterion... + for ( int i = 0 ; i < m_Criteria.Count() ; ++i ) + { + retval = RecursiveGetValueForRuleCriterionByName( pSystem, &pSystem->m_Criteria[m_Criteria[i]], pCritNameSym ); + if ( retval != NULL ) + { + // we found a result, early out + break; + } + } + + return retval; +} + +const Criteria *Rule::GetPointerForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym ) +{ + const Criteria * retval = NULL; + // for each rule criterion... + for ( int i = 0 ; i < m_Criteria.Count() ; ++i ) + { + retval = RecursiveGetPointerForRuleCriterionByName( pSystem, &pSystem->m_Criteria[m_Criteria[i]], pCritNameSym ); + if ( retval != NULL ) + { + // we found a result, early out + break; + } + } + + return retval; +} + +const char *Rule::RecursiveGetValueForRuleCriterionByName( CResponseSystem * RESTRICT pSystem, + const Criteria * RESTRICT pCrit, const CUtlSymbol &pCritNameSym ) +{ + Assert( pCrit ); + if ( !pCrit ) return NULL; + if ( pCrit->IsSubCriteriaType() ) + { + // test each of the children (depth first) + const char *pRet = NULL; + for ( int i = 0 ; i < pCrit->subcriteria.Count() ; ++i ) + { + pRet = RecursiveGetValueForRuleCriterionByName( pSystem, &pSystem->m_Criteria[pCrit->subcriteria[i]], pCritNameSym ); + if ( pRet ) // if found something, early out + return pRet; + } + } + else // leaf criterion + { + if ( pCrit->nameSym == pCritNameSym ) + { + return pCrit->value; + } + else + { + return NULL; + } + } + + return NULL; +} + + +const Criteria *Rule::RecursiveGetPointerForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym ) +{ + Assert( pCrit ); + if ( !pCrit ) return NULL; + if ( pCrit->IsSubCriteriaType() ) + { + // test each of the children (depth first) + const Criteria *pRet = NULL; + for ( int i = 0 ; i < pCrit->subcriteria.Count() ; ++i ) + { + pRet = RecursiveGetPointerForRuleCriterionByName( pSystem, &pSystem->m_Criteria[pCrit->subcriteria[i]], pCritNameSym ); + if ( pRet ) // if found something, early out + return pRet; + } + } + else // leaf criterion + { + if ( pCrit->nameSym == pCritNameSym ) + { + return pCrit; + } + else + { + return NULL; + } + } + + return NULL; +} + + +static void CC_RR_Debug_ResponseConcept_Exclude( const CCommand &args ) +{ + // shouldn't use this extern elsewhere -- it's meant to be a hidden + // implementation detail + extern CRR_ConceptSymbolTable *g_pRRConceptTable; + Assert( g_pRRConceptTable ); + if ( !g_pRRConceptTable ) return; + + + // different things for different argument lengths + switch ( args.ArgC() ) + { + case 0: + { + AssertMsg( args.ArgC() > 0, "WTF error in ccommand parsing: zero arguments!\n" ); + return; + } + case 1: + { + // print usage info + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "Usage: rr_debugresponseconcept_exclude Concept1 Concept2 Concept3...\n"); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\tseparate multiple concepts with spaces.\n"); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\tcall with no arguments to see this message and a list of current excludes.\n"); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\tto reset the exclude list, type \"rr_debugresponseconcept_exclude !\"\n"); + + // print current excludes + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\nCurrent exclude list:\n" ); + if ( !CResponseSystem::m_DebugExcludeList.IsValidIndex( CResponseSystem::m_DebugExcludeList.Head() ) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t\n" ); + } + else + { + CResponseSystem::ExcludeList_t::IndexLocalType_t i; + for ( i = CResponseSystem::m_DebugExcludeList.Head() ; + CResponseSystem::m_DebugExcludeList.IsValidIndex(i) ; + i = CResponseSystem::m_DebugExcludeList.Next(i) ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t%s\n", CResponseSystem::m_DebugExcludeList[i].GetStringConcept() ); + } + } + return; + } + case 2: + // deal with the erase operator + if ( args[1][0] == '!' ) + { + CResponseSystem::m_DebugExcludeList.Purge(); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "Exclude list emptied.\n" ); + return; + } + // else, FALL THROUGH: + default: + // add each arg to the exclude list + for ( int i = 1 ; i < args.ArgC() ; ++i ) + { + if ( !g_pRRConceptTable->Find(args[i]).IsValid() ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t'%s' is not a known concept (adding it anyway)\n", args[i] ); + } + CRR_Concept concept( args[i] ); + CResponseSystem::m_DebugExcludeList.AddToTail( concept ); + } + } +} +#if RR_DUMPHASHINFO_ENABLED +void ResponseRulePartition::PrintBucketInfo( CResponseSystem *pSys ) +{ + struct bucktuple_t + { + int nBucket; + int nCount; + bucktuple_t() : nBucket(-1), nCount(-1) {}; + bucktuple_t( int bucket, int count ) : nBucket(bucket), nCount(count) {}; + + static int __cdecl SortCompare( const bucktuple_t * a, const bucktuple_t * b ) + { + return a->nCount - b->nCount; + } + }; + + CUtlVector infos( N_RESPONSE_PARTITIONS, N_RESPONSE_PARTITIONS ); + + float nAverage = 0; + for ( int i = 0 ; i < N_RESPONSE_PARTITIONS ; ++i ) + { + int count = m_RuleParts[i].Count(); + infos.AddToTail( bucktuple_t( i, count ) ); + nAverage += count; + } + nAverage /= N_RESPONSE_PARTITIONS; + infos.Sort( bucktuple_t::SortCompare ); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%d buckets, %d total, %.2f average size\n", N_RESPONSE_PARTITIONS, Count(), nAverage ); + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "8 shortest buckets:\n" ); + for ( int i = 0 ; i < 8 ; ++i ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t%d: %d\n", infos[i].nBucket, infos[i].nCount ); + } + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "8 longest buckets:\n" ); + for ( int i = infos.Count() - 1 ; i >= infos.Count() - 9 ; --i ) + { + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "\t%d: %d\n", infos[i].nBucket, infos[i].nCount ); + } + int nempty = 0; + for ( nempty = 0 ; nempty < infos.Count() ; ++nempty ) + { + if ( infos[nempty].nCount != 0 ) + break; + } + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%d empty buckets\n", nempty ); + + /* + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, " Contents of longest bucket\nwho\tconcept\n" ); + tRuleDict &bucket = m_RuleParts[infos[infos.Count()-1].nBucket]; + for ( tRuleDict::IndexType_t i = bucket.FirstInorder(); bucket.IsValidIndex(i); i = bucket.NextInorder(i) ) + { + Rule &rule = bucket.Element(i) ; + CGMsg( 0, CON_GROUP_RESPONSE_SYSTEM, "%s\t%s\n", rule.GetValueForRuleCriterionByName( pSys, "who" ), rule.GetValueForRuleCriterionByName( pSys, CriteriaSet::ComputeCriteriaSymbol("concept") ) ); + } + */ +} +#endif \ No newline at end of file diff --git a/responserules/runtime/response_system.h b/responserules/runtime/response_system.h new file mode 100644 index 000000000..a862d7614 --- /dev/null +++ b/responserules/runtime/response_system.h @@ -0,0 +1,336 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: The CResponseSystem class. Don't include this header; include the response_types +// into which it is transcluded. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_SYSTEM_H +#define RESPONSE_SYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utldict.h" + +namespace ResponseRules +{ + typedef ResponseParams AI_ResponseParams ; + #define AI_CriteriaSet ResponseRules::CriteriaSet + + //----------------------------------------------------------------------------- + // Purpose: The database of all available responses. + // The Rules are partitioned based on a variety of factors (presently, + // speaker and concept) for faster lookup, basically a seperate-chained hash. + //----------------------------------------------------------------------------- + class CResponseSystem : public IResponseSystem + { + public: + CResponseSystem(); + ~CResponseSystem(); + + typedef void (CResponseSystem::*pfnResponseDispatch)( void ); + typedef void (CResponseSystem::*pfnParseRuleDispatch)( Rule & ); + typedef void (CResponseSystem::*pfnParseResponseDispatch)( ParserResponse &, ResponseGroup&, AI_ResponseParams * ); + typedef void (CResponseSystem::*pfnParseResponseGroupDispatch) ( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + + typedef CUtlMap< unsigned,pfnResponseDispatch > DispatchMap_t; + typedef CUtlMap< unsigned,pfnParseRuleDispatch > ParseRuleDispatchMap_t; + typedef CUtlMap< unsigned,pfnParseResponseDispatch > ParseResponseDispatchMap_t; + typedef CUtlMap< unsigned,pfnParseResponseGroupDispatch > ParseResponseGroupDispatchMap_t; + +#pragma region IResponseSystem + // IResponseSystem + virtual bool FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter = NULL ); + virtual void GetAllResponses( CUtlVector *pResponses ); + +#ifdef MAPBASE + virtual void SetProspective( bool bToggle ) { m_bInProspective = bToggle; } + + virtual void MarkResponseAsUsed( short iGroup, short iWithinGroup ); +#endif +#pragma endregion Implement interface from IResponseSystem + + virtual void Release() = 0; + + virtual void DumpRules(); + + bool IsCustomManagable() { return m_bCustomManagable; } + +#ifdef MAPBASE + virtual +#endif + void Clear(); + + void DumpDictionary( const char *pszName ); + + protected: + + void BuildDispatchTables(); + bool Dispatch( char const *pToken, unsigned int uiHash, DispatchMap_t &rMap ); + bool DispatchParseRule( char const *pToken, unsigned int uiHash, ParseRuleDispatchMap_t &rMap, Rule &newRule ); + bool DispatchParseResponse( char const *pToken, unsigned int uiHash, ParseResponseDispatchMap_t &rMap, ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + bool DispatchParseResponseGroup( char const *pToken, unsigned int uiHash, ParseResponseGroupDispatchMap_t &rMap, char const *responseGroupName, ResponseGroup& newGroup, AI_ResponseParams &groupResponseParams ); + + virtual const char *GetScriptFile( void ) = 0; + void LoadRuleSet( const char *setname ); + + void ResetResponseGroups(); + + float LookForCriteria( const CriteriaSet &criteriaSet, int iCriteria ); + float RecursiveLookForCriteria( const CriteriaSet &criteriaSet, Criteria *pParent ); + + public: + + void CopyRuleFrom( Rule *pSrcRule, ResponseRulePartition::tIndex iRule, CResponseSystem *pCustomSystem ); + void CopyCriteriaFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ); + void CopyResponsesFrom( Rule *pSrcRule, Rule *pDstRule, CResponseSystem *pCustomSystem ); + void CopyEnumerationsFrom( CResponseSystem *pCustomSystem ); + + //private: + + struct Enumeration + { + float value; + }; + + struct ResponseSearchResult + { + ResponseSearchResult() + { + group = NULL; + action = NULL; + } + + ResponseGroup *group; + ParserResponse *action; + }; + + inline bool ParseToken( void ) + { + if ( m_bUnget ) + { + m_bUnget = false; + return true; + } + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + m_ScriptStack[ 0 ].currenttoken = IEngineEmulator::Get()->ParseFile( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); + m_ScriptStack[ 0 ].tokencount++; + return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; + } + +#ifdef MAPBASE + inline bool ParseTokenIntact( void ) + { + if ( m_bUnget ) + { + m_bUnget = false; + return true; + } + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + m_ScriptStack[ 0 ].currenttoken = IEngineEmulator::Get()->ParseFilePreserve( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); + m_ScriptStack[ 0 ].tokencount++; + return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; + } +#endif + + inline void Unget() + { + m_bUnget = true; + } + + inline bool TokenWaiting( void ) + { + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + const char *p = m_ScriptStack[ 0 ].currenttoken; + + if ( !p ) + { + Error( "AI_ResponseSystem: Unxpected TokenWaiting() with NULL buffer in %s", (char * ) m_ScriptStack[ 0 ].name ); + return false; + } + + + while ( *p && *p!='\n') + { + // Special handler for // comment blocks + if ( *p == '/' && *(p+1) == '/' ) + return false; + + if ( !V_isspace( *p ) || isalnum( *p ) ) + return true; + + p++; + } + + return false; + } + + void ParseOneResponse( const char *responseGroupName, ResponseGroup& group, ResponseParams *defaultParams = NULL ); + + void ParseInclude( void ); + void ParseResponse( void ); + void ParseCriterion( void ); + void ParseRule( void ); + void ParseEnumeration( void ); + + private: + void ParseRule_MatchOnce( Rule &newRule ); + void ParseRule_ApplyContextToWorld( Rule &newRule ); +#ifdef MAPBASE + void ParseRule_ApplyContextToSquad( Rule &newRule ); + void ParseRule_ApplyContextToEnemy( Rule &newRule ); +#endif + void ParseRule_ApplyContext( Rule &newRule ); + void ParseRule_Response( Rule &newRule ); + //void ParseRule_ForceWeight( Rule &newRule ); + void ParseRule_Criteria( Rule &newRule ); + char const *m_pParseRuleName; + bool m_bParseRuleValid; + + void ParseResponse_Weight( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_PreDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_NoDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_DefaultDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Delay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_SpeakOnce( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_NoScene( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_StopOnNonIdle( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Odds( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_RespeakDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_WeaponDelay( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Soundlevel( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_DisplayFirst( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_DisplayLast( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Fire( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + void ParseResponse_Then( ParserResponse &newResponse, ResponseGroup& group, AI_ResponseParams *rp ); + + void ParseResponseGroup_Start( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_PreDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_NoDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_DefaultDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_Delay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_SpeakOnce( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_NoScene( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_StopOnNonIdle( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_Odds( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_RespeakDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_WeaponDelay( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + void ParseResponseGroup_Soundlevel( char const *responseGroupName, ResponseGroup &newGroup, AI_ResponseParams &groupResponseParams ); + +public: + int ParseOneCriterion( const char *criterionName ); + + bool Compare( const char *setValue, Criteria *c, bool verbose = false ); + bool CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose = false ); + void ComputeMatcher( Criteria *c, Matcher& matcher ); + void ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ); + float LookupEnumeration( const char *name, bool& found ); + + ResponseRulePartition::tIndex FindBestMatchingRule( const CriteriaSet& set, bool verbose, float &scoreOfBestMatchingRule ); + +#ifdef MAPBASE + void DisableEmptyRules(); +#endif + + float ScoreCriteriaAgainstRule( const CriteriaSet& set, ResponseRulePartition::tRuleDict &dict, int irule, bool verbose = false ); + float RecursiveScoreSubcriteriaAgainstRule( const CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ); + float ScoreCriteriaAgainstRuleCriteria( const CriteriaSet& set, int icriterion, bool& exclude, bool verbose = false ); + void FakeDepletes( ResponseGroup *g, IResponseFilter *pFilter ); + void RevertFakedDepletes( ResponseGroup *g ); + bool GetBestResponse( ResponseSearchResult& result, Rule *rule, bool verbose = false, IResponseFilter *pFilter = NULL ); + bool ResolveResponse( ResponseSearchResult& result, int depth, const char *name, bool verbose = false, IResponseFilter *pFilter = NULL ); + int SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ); + void DescribeResponseGroup( ResponseGroup *group, int selected, int depth ); + void DebugPrint( int depth, const char *fmt, ... ); + + void LoadFromBuffer( const char *scriptfile, const char *buffer ); + + void GetCurrentScript( char *buf, size_t buflen ); + int GetCurrentToken() const; + void SetCurrentScript( const char *script ); + + inline bool IsRootCommand( unsigned int hash ) const + { + int slot = m_RootCommandHashes.Find( hash ); + return slot != m_RootCommandHashes.InvalidIndex(); + } + + inline bool IsRootCommand() const + { + return IsRootCommand( RR_HASH( token ) ); + } + + void PushScript( const char *scriptfile, unsigned char *buffer ); + void PopScript(void); + + void ResponseWarning( const char *fmt, ... ); + + CUtlDict< ResponseGroup, short > m_Responses; + CUtlDict< Criteria, short > m_Criteria; + // CUtlDict< Rule, short > m_Rules; + ResponseRulePartition m_RulePartitions; + CUtlDict< Enumeration, short > m_Enumerations; + + CUtlVector m_FakedDepletes; + + char token[ 1204 ]; + + bool m_bUnget; + + bool m_bCustomManagable; + +#ifdef MAPBASE + // This is a hack specifically designed to fix displayfirst, speakonce, etc. in "prospective" response searches, + // especially the prospective lookups in followup responses. + // It works by preventing responses from being marked as "used". + bool m_bInProspective; +#endif + + struct ScriptEntry + { + unsigned char *buffer; + FileNameHandle_t name; + const char *currenttoken; + int tokencount; + }; + + CUtlVector< ScriptEntry > m_ScriptStack; + CStringPool m_IncludedFiles; + + DispatchMap_t m_FileDispatch; + ParseRuleDispatchMap_t m_RuleDispatch; + ParseResponseDispatchMap_t m_ResponseDispatch; + ParseResponseGroupDispatchMap_t m_ResponseGroupDispatch; + CUtlRBTree< unsigned int > m_RootCommandHashes; + + // for debugging purposes only: concepts to be emitted from rr_debugresponses 2 + typedef CUtlLinkedList< CRR_Concept, unsigned short, false, unsigned int > ExcludeList_t; + static ExcludeList_t m_DebugExcludeList; + + friend class CDefaultResponseSystemSaveRestoreBlockHandler; + friend class CResponseSystemSaveRestoreOps; + }; + + // Some globals inherited from AI_Speech.h: + const float AIS_DEF_MIN_DELAY = 2.8; // Minimum amount of time an NPCs will wait after someone has spoken before considering speaking again + const float AIS_DEF_MAX_DELAY = 3.2; // Maximum amount of time an NPCs will wait after someone has spoken before considering speaking again +} + +#endif // RESPONSE_SYSTEM_H \ No newline at end of file diff --git a/responserules/runtime/response_types.cpp b/responserules/runtime/response_types.cpp new file mode 100644 index 000000000..1d8f6b311 --- /dev/null +++ b/responserules/runtime/response_types.cpp @@ -0,0 +1,281 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" + +#include "tier1/mapbase_con_groups.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace ResponseRules; + + +// bizarre function handed down from the misty days of yore +// and the original response system. a lot of stuff uses it +// and I can't be arsed to replace everything with the c stdlib +// stuff +namespace ResponseRules +{ + extern const char *ResponseCopyString( const char *in ); +}; + + +//-------------------- MATCHER ---------------------------------------------- + +Matcher::Matcher() +{ + valid = false; + isnumeric = false; + notequal = false; + usemin = false; + minequals = false; + usemax = false; + maxequals = false; +#ifdef MAPBASE + isbit = false; +#endif + maxval = 0.0f; + minval = 0.0f; + + token = UTL_INVAL_SYMBOL; + rawtoken = UTL_INVAL_SYMBOL; +} + +void Matcher::Describe( void ) +{ + if ( !valid ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " invalid!\n" ); + return; + } + char sz[ 128 ]; + + sz[ 0] = 0; + int minmaxcount = 0; + if ( usemin ) + { + Q_snprintf( sz, sizeof( sz ), ">%s%.3f", minequals ? "=" : "", minval ); + minmaxcount++; + } + if ( usemax ) + { + char sz2[ 128 ]; + Q_snprintf( sz2, sizeof( sz2 ), "<%s%.3f", maxequals ? "=" : "", maxval ); + + if ( minmaxcount > 0 ) + { + Q_strncat( sz, " and ", sizeof( sz ), COPY_ALL_CHARACTERS ); + } + Q_strncat( sz, sz2, sizeof( sz ), COPY_ALL_CHARACTERS ); + minmaxcount++; + } + + if ( minmaxcount >= 1 ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: %s\n", sz ); + return; + } + +#ifdef MAPBASE + if ( isbit ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: &%s%s\n", notequal ? "~" : "", GetToken() ); + return; + } +#endif + + if ( notequal ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: !=%s\n", GetToken() ); + return; + } + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, " matcher: ==%s\n", GetToken() ); +} + +void Matcher::SetToken( char const *s ) +{ + token = g_RS.AddString( s ); +} + +void Matcher::SetRaw( char const *raw ) +{ + rawtoken = g_RS.AddString( raw ); +} + +char const *Matcher::GetToken() +{ + if ( token.IsValid() ) + { + return g_RS.String( token ); + } + return ""; +} + +char const *Matcher::GetRaw() +{ + if ( rawtoken.IsValid() ) + { + return g_RS.String( rawtoken ); + } + return ""; +} + +//-------------------- CRITERIA ---------------------------------------------- + +Criteria::Criteria() +{ + value = NULL; + weight.SetFloat( 1.0f ); + required = false; +} +Criteria::Criteria(const Criteria& src ) +{ + operator=( src ); +} + +Criteria::~Criteria() +{ + // do nothing because we don't own name and value anymore +} + +Criteria& Criteria::operator =(const Criteria& src ) +{ + if ( this == &src ) + return *this; + + nameSym = src.nameSym; + value = ResponseCopyString( src.value ); + weight = src.weight; + required = src.required; + + matcher = src.matcher; + + int c = src.subcriteria.Count(); + subcriteria.EnsureCapacity( c ); + for ( int i = 0; i < c; i++ ) + { + subcriteria.AddToTail( src.subcriteria[ i ] ); + } + + return *this; +} + + +//-------------------- RESPONSE ---------------------------------------------- + + + +ParserResponse::ParserResponse() : m_followup() +{ + type = RESPONSE_NONE; + value = NULL; + weight.SetFloat( 1.0f ); + depletioncount = 0; + first = false; + last = false; +} + +ParserResponse& ParserResponse::operator =( const ParserResponse& src ) +{ + if ( this == &src ) + return *this; + weight = src.weight; + type = src.type; + value = ResponseCopyString( src.value ); + depletioncount = src.depletioncount; + first = src.first; + last = src.last; + params = src.params; + + m_followup.followup_concept = ResponseCopyString(src.m_followup.followup_concept); + m_followup.followup_contexts = ResponseCopyString(src.m_followup.followup_contexts); + m_followup.followup_target = ResponseCopyString(src.m_followup.followup_target); + m_followup.followup_entityioinput = ResponseCopyString(src.m_followup.followup_entityioinput); + m_followup.followup_entityiotarget = ResponseCopyString(src.m_followup.followup_entityiotarget); + m_followup.followup_delay = src.m_followup.followup_delay; + m_followup.followup_entityiodelay = src.m_followup.followup_entityiodelay; + + return *this; +} + +ParserResponse::ParserResponse( const ParserResponse& src ) +{ + operator=( src ); +} + +ParserResponse::~ParserResponse() +{ + // nothing to do, since we don't own + // the strings anymore +} + +// ------------ RULE --------------- + +Rule::Rule() : m_nForceWeight(0) +{ + m_bMatchOnce = false; + m_bEnabled = true; + m_szContext = NULL; +#ifdef MAPBASE + m_iContextFlags = 0; +#else + m_bApplyContextToWorld = false; +#endif +} + +Rule& Rule::operator =( const Rule& src ) +{ + if ( this == &src ) + return *this; + + int i; + int c; + + c = src.m_Criteria.Count(); + m_Criteria.EnsureCapacity( c ); + for ( i = 0; i < c; i++ ) + { + m_Criteria.AddToTail( src.m_Criteria[ i ] ); + } + + c = src.m_Responses.Count(); + m_Responses.EnsureCapacity( c ); + for ( i = 0; i < c; i++ ) + { + m_Responses.AddToTail( src.m_Responses[ i ] ); + } + + SetContext( src.m_szContext ); + m_bMatchOnce = src.m_bMatchOnce; + m_bEnabled = src.m_bEnabled; +#ifdef MAPBASE + m_iContextFlags = src.m_iContextFlags; +#else + m_bApplyContextToWorld = src.m_bApplyContextToWorld; +#endif + m_nForceWeight = src.m_nForceWeight; + return *this; +} + +Rule::Rule( const Rule& src ) +{ + operator=(src); +} + +Rule::~Rule() +{ +} + +void Rule::SetContext( const char *context ) +{ + // we don't own the data we point to, so just update pointer + m_szContext = ResponseCopyString( context ); +} + + diff --git a/responserules/runtime/response_types_internal.cpp b/responserules/runtime/response_types_internal.cpp new file mode 100644 index 000000000..873f87592 --- /dev/null +++ b/responserules/runtime/response_types_internal.cpp @@ -0,0 +1,172 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" +#ifdef MAPBASE +#include "convar.h" +#include "mapbase_matchers_base.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace ResponseRules; + +#ifdef MAPBASE +ConVar rr_bucket_name_who( "rr_bucket_name_who", "Classname", FCVAR_NONE, "The name of the criteria to use for the 'Who' bucket." ); // Default changed to "Classname" for HL2 +ConVar rr_bucket_name_concept( "rr_bucket_name_concept", "Concept", FCVAR_NONE, "The name of the criteria to use for the 'Concept' bucket." ); +ConVar rr_bucket_name_subject( "rr_bucket_name_subject", "Subject", FCVAR_NONE, "The name of the criteria to use for the 'Subject' bucket." ); +#endif + + + + +ResponseRulePartition::ResponseRulePartition() +{ + Assert(true); + COMPILE_TIME_ASSERT( kIDX_ELEM_MASK < (1 << 16) ); + COMPILE_TIME_ASSERT( (kIDX_ELEM_MASK & (kIDX_ELEM_MASK + 1)) == 0 ); /// assert is power of two minus one +} + +ResponseRulePartition::~ResponseRulePartition() +{ + RemoveAll(); +} + +ResponseRulePartition::tIndex ResponseRulePartition::IndexFromDictElem( tRuleDict* pDict, int elem ) +{ + Assert( pDict ); + // If this fails, you've tried to build an index for a rule that's not stored + // in this partition + Assert( pDict >= m_RuleParts && pDict < m_RuleParts + N_RESPONSE_PARTITIONS ); + AssertMsg1( elem <= kIDX_ELEM_MASK, "A rule dictionary has %d elements; this exceeds the 255 that can be packed into an index.\n", elem ); + + int bucket = pDict - m_RuleParts; + return ( bucket << 16 ) | ( elem & kIDX_ELEM_MASK ); // this is a native op on PPC +} + + +char const *ResponseRulePartition::GetElementName( const tIndex &i ) const +{ + Assert( IsValid(i) ); + return m_RuleParts[ BucketFromIdx(i) ].GetElementName( PartFromIdx(i) ); +} + + +Rule *ResponseRulePartition::FindByName( char const *name ) const +{ + int count; + + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + count = m_RuleParts[bukkit].Count(); + for ( int i = 0; i < count; ++i ) + { + if (V_strncmp( m_RuleParts[bukkit].GetElementName(i), name, CRR_Response::MAX_RULE_NAME ) == 0) + return m_RuleParts[bukkit][i]; + } + } + + return NULL; +} + + +int ResponseRulePartition::Count( void ) +{ + int count = 0 ; + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + count += m_RuleParts[bukkit].Count(); + } + + return count; +} + +void ResponseRulePartition::RemoveAll( void ) +{ + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + for ( int i = m_RuleParts[bukkit].FirstInorder(); i != m_RuleParts[bukkit].InvalidIndex(); i = m_RuleParts[bukkit].NextInorder( i ) ) + { + delete m_RuleParts[bukkit][ i ]; + } + m_RuleParts[bukkit].RemoveAll(); + } +} + +#ifdef MAPBASE +void ResponseRulePartition::PurgeAndDeleteElements() +{ + for ( int bukkit = 0 ; bukkit < N_RESPONSE_PARTITIONS ; ++bukkit ) + { + for ( int i = m_RuleParts[bukkit].FirstInorder(); i != m_RuleParts[bukkit].InvalidIndex(); i = m_RuleParts[bukkit].NextInorder( i ) ) + { + delete m_RuleParts[bukkit][ i ]; + } + m_RuleParts[bukkit].Purge(); + } +} +#endif + +// don't bucket "subject" criteria that prefix with operators, since stripping all that out again would +// be a big pain, and the most important rules that need subjects are tlk_remarks anyway. +static inline bool CanBucketBySubject( const char * RESTRICT pszSubject ) +{ + return pszSubject && + ( ( pszSubject[0] >= 'A' && pszSubject[0] <= 'Z' ) || + ( pszSubject[0] >= 'a' && pszSubject[0] <= 'z' ) ) +#ifdef MAPBASE + && !Matcher_ContainsWildcard( pszSubject ) +#endif + ; +} + +ResponseRulePartition::tRuleDict &ResponseRulePartition::GetDictForRule( CResponseSystem *pSystem, Rule *pRule ) +{ +#ifdef MAPBASE + const static CUtlSymbol kWHO = CriteriaSet::ComputeCriteriaSymbol( rr_bucket_name_who.GetString() ); + const static CUtlSymbol kCONCEPT = CriteriaSet::ComputeCriteriaSymbol( rr_bucket_name_concept.GetString() ); + const static CUtlSymbol kSUBJECT = CriteriaSet::ComputeCriteriaSymbol( rr_bucket_name_subject.GetString() ); +#else + const static CUtlSymbol kWHO = CriteriaSet::ComputeCriteriaSymbol("Who"); + const static CUtlSymbol kCONCEPT = CriteriaSet::ComputeCriteriaSymbol("Concept"); + const static CUtlSymbol kSUBJECT = CriteriaSet::ComputeCriteriaSymbol("Subject"); +#endif + + const char *pszSpeaker = pRule->GetValueForRuleCriterionByName( pSystem, kWHO ); + const char *pszConcept = pRule->GetValueForRuleCriterionByName( pSystem, kCONCEPT ); + const Criteria *pSubjCrit = pRule->GetPointerForRuleCriterionByName( pSystem, kSUBJECT ); + + return m_RuleParts[ + GetBucketForSpeakerAndConcept( pszSpeaker, pszConcept, + ( pSubjCrit && pSubjCrit->required && CanBucketBySubject(pSubjCrit->value) ) ? + pSubjCrit->value : + NULL ) + ]; +} + + +void ResponseRulePartition::GetDictsForCriteria( CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > *pResult, const CriteriaSet &criteria ) +{ + pResult->RemoveAll(); + pResult->EnsureCapacity( 2 ); + + // get the values for Who and Concept, which are what we bucket on + int speakerIdx = criteria.FindCriterionIndex( "Who" ); + const char *pszSpeaker = speakerIdx != -1 ? criteria.GetValue( speakerIdx ) : NULL ; + + int conceptIdx = criteria.FindCriterionIndex( "Concept" ); + const char *pszConcept = conceptIdx != -1 ? criteria.GetValue( conceptIdx ) : NULL ; + + int subjectIdx = criteria.FindCriterionIndex( "Subject" ); + const char *pszSubject = subjectIdx != -1 ? criteria.GetValue( subjectIdx ) : NULL ; + + pResult->AddToTail( &m_RuleParts[ GetBucketForSpeakerAndConcept(pszSpeaker, pszConcept, pszSubject) ] ); + // also try the rules not specifying subject + pResult->AddToTail( &m_RuleParts[ GetBucketForSpeakerAndConcept(pszSpeaker, pszConcept, NULL) ] ); + +} \ No newline at end of file diff --git a/responserules/runtime/response_types_internal.h b/responserules/runtime/response_types_internal.h new file mode 100644 index 000000000..08f311855 --- /dev/null +++ b/responserules/runtime/response_types_internal.h @@ -0,0 +1,560 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_TYPES_INTERNAL_H +#define RESPONSE_TYPES_INTERNAL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "responserules/response_types.h" +#include "utldict.h" + + +namespace ResponseRules +{ + + inline unsigned FASTCALL HashStringConventional( const char *pszKey ) + { + unsigned hash = 0xAAAAAAAA; // Alternating 1's and 0's to maximize the effect of the later multiply and add + + for( ; *pszKey ; pszKey++ ) + { + hash = ( ( hash << 5 ) + hash ) + (uint8)(*pszKey); + } + + return hash; + } + + // Note: HashString causes collisions!!! +#define RR_HASH HashStringConventional + +#pragma pack(push,1) + + class Matcher + { + public: + Matcher(); + + void Describe( void ); + + float maxval; + float minval; + + bool valid : 1; //1 + bool isnumeric : 1; //2 + bool notequal : 1; //3 + bool usemin : 1; //4 + bool minequals : 1; //5 + bool usemax : 1; //6 + bool maxequals : 1; //7 +#ifdef MAPBASE + bool isbit : 1; //8 +#endif + + void SetToken( char const *s ); + + char const *GetToken(); + + void SetRaw( char const *raw ); + + char const *GetRaw(); + + private: + CUtlSymbol token; + CUtlSymbol rawtoken; + }; +#pragma pack(pop) + + struct Criteria + { + Criteria(); + Criteria& operator =(const Criteria& src ); + + Criteria(const Criteria& src ); + ~Criteria(); + + // Does this criterion recursively contain more criteria? + inline bool IsSubCriteriaType() const + { + return ( subcriteria.Count() > 0 ) ? true : false; + } + + // const char *name; + CUtlSymbol nameSym; + const char *value; + float16 weight; + bool required; + + Matcher matcher; + + // Indices into sub criteria + CUtlVectorConservative< unsigned short > subcriteria; + }; + +#pragma pack(push,1) + /// This is a response block as read from the file, + /// different from CRR_Response which is what is handed + /// back to queries. + struct ParserResponse + { + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + ParserResponse(); + ParserResponse( const ParserResponse& src ); + ParserResponse& operator =( const ParserResponse& src ); + ~ParserResponse(); + + ResponseType_t GetType() { return (ResponseType_t)type; } + + ResponseParams params; + + const char *value; // fixed up value spot // 4 + float16 weight; // 6 + + byte depletioncount; // 7 + byte type : 6; // 8 + byte first : 1; // + byte last : 1; // + + ALIGN32 AI_ResponseFollowup m_followup; // info on whether I should force the other guy to say something + }; +#pragma pack(pop) + +#pragma pack(push,1) + struct ResponseGroup + { + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + ResponseGroup() + { + // By default visit all nodes before repeating + m_bSequential = false; + m_bNoRepeat = false; + m_bEnabled = true; + m_nCurrentIndex = 0; + m_bDepleteBeforeRepeat = true; + m_nDepletionCount = 1; + m_bHasFirst = false; + m_bHasLast = false; + } + + ResponseGroup( const ResponseGroup& src ) + { + int c = src.group.Count(); + for ( int i = 0; i < c; i++ ) + { + group.AddToTail( src.group[ i ] ); + } + + m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; + m_nDepletionCount = src.m_nDepletionCount; + m_bHasFirst = src.m_bHasFirst; + m_bHasLast = src.m_bHasLast; + m_bSequential = src.m_bSequential; + m_bNoRepeat = src.m_bNoRepeat; + m_bEnabled = src.m_bEnabled; + m_nCurrentIndex = src.m_nCurrentIndex; + } + + ResponseGroup& operator=( const ResponseGroup& src ) + { + if ( this == &src ) + return *this; + int c = src.group.Count(); + for ( int i = 0; i < c; i++ ) + { + group.AddToTail( src.group[ i ] ); + } + + m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; + m_nDepletionCount = src.m_nDepletionCount; + m_bHasFirst = src.m_bHasFirst; + m_bHasLast = src.m_bHasLast; + m_bSequential = src.m_bSequential; + m_bNoRepeat = src.m_bNoRepeat; + m_bEnabled = src.m_bEnabled; + m_nCurrentIndex = src.m_nCurrentIndex; + return *this; + } + + bool HasUndepletedChoices() const + { + if ( !m_bDepleteBeforeRepeat ) + return true; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + if ( group[ i ].depletioncount != m_nDepletionCount ) + return true; + } + + return false; + } + + void MarkResponseUsed( int idx ) + { + if ( !m_bDepleteBeforeRepeat ) + return; + + if ( idx < 0 || idx >= group.Count() ) + { + Assert( 0 ); + return; + } + + group[ idx ].depletioncount = m_nDepletionCount; + } + + void ResetDepletionCount() + { + if ( !m_bDepleteBeforeRepeat ) + return; + ++m_nDepletionCount; + } + + void Reset() + { + ResetDepletionCount(); + SetEnabled( true ); + SetCurrentIndex( 0 ); + m_nDepletionCount = 1; + + for ( int i = 0; i < group.Count(); ++i ) + { + group[ i ].depletioncount = 0; + } + } + + bool HasUndepletedFirst( int& index ) + { + index = -1; + + if ( !m_bDepleteBeforeRepeat ) + return false; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &group[ i ]; + + if ( ( r->depletioncount != m_nDepletionCount ) && r->first ) + { + index = i; + return true; + } + } + + return false; + } + + bool HasUndepletedLast( int& index ) + { + index = -1; + + if ( !m_bDepleteBeforeRepeat ) + return false; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + ParserResponse *r = &group[ i ]; + + if ( ( r->depletioncount != m_nDepletionCount ) && r->last ) + { + index = i; + return true; + } + } + + return false; + } + + bool ShouldCheckRepeats() const { return m_bDepleteBeforeRepeat; } + int GetDepletionCount() const { return m_nDepletionCount; } + + bool IsSequential() const { return m_bSequential; } + void SetSequential( bool seq ) { m_bSequential = seq; } + + bool IsNoRepeat() const { return m_bNoRepeat; } + void SetNoRepeat( bool norepeat ) { m_bNoRepeat = norepeat; } + + bool IsEnabled() const { return m_bEnabled; } + void SetEnabled( bool enabled ) { m_bEnabled = enabled; } + + int GetCurrentIndex() const { return m_nCurrentIndex; } + void SetCurrentIndex( byte idx ) { m_nCurrentIndex = idx; } + + CUtlVector< ParserResponse > group; + + bool m_bEnabled; + + byte m_nCurrentIndex; + // Invalidation counter + byte m_nDepletionCount; + + // Use all slots before repeating any + bool m_bDepleteBeforeRepeat : 1; + bool m_bHasFirst : 1; + bool m_bHasLast : 1; + bool m_bSequential : 1; + bool m_bNoRepeat : 1; + }; +#pragma pack(pop) + +#pragma pack(push,1) + struct Rule + { + Rule(); + Rule( const Rule& src ); + ~Rule(); + Rule& operator =( const Rule& src ); + + void SetContext( const char *context ); + + const char *GetContext( void ) const { return m_szContext; } + + inline bool IsEnabled() const { return m_bEnabled; } + inline void Disable() { m_bEnabled = false; } + inline bool IsMatchOnce() const { return m_bMatchOnce; } +#ifdef MAPBASE + inline int GetContextFlags() const { return m_iContextFlags; } + inline bool IsApplyContextToWorld() const { return (m_iContextFlags & APPLYCONTEXT_WORLD) != 0; } +#else + inline bool IsApplyContextToWorld() const { return m_bApplyContextToWorld; } +#endif + + const char *GetValueForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym ); + const Criteria *GetPointerForRuleCriterionByName( CResponseSystem *pSystem, const CUtlSymbol &pCritNameSym ); + + // Indices into underlying criteria and response dictionaries + CUtlVectorConservative< unsigned short > m_Criteria; + CUtlVectorConservative< unsigned short> m_Responses; + + const char *m_szContext; + uint8 m_nForceWeight; + +#ifdef MAPBASE + // TODO: Could this cause any issues with the code optimization? + uint8 m_iContextFlags; +#else + bool m_bApplyContextToWorld : 1; +#endif + + bool m_bMatchOnce : 1; + bool m_bEnabled : 1; + + private: + // what is this, lisp? + const char *RecursiveGetValueForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym ); + const Criteria *RecursiveGetPointerForRuleCriterionByName( CResponseSystem *pSystem, const Criteria *pCrit, const CUtlSymbol &pCritNameSym ); + }; +#pragma pack(pop) + + template + class CResponseDict : public CUtlMap + { + public: + CResponseDict() : CUtlMap( DefLessFunc( unsigned int ) ), m_ReverseMap( DefLessFunc( unsigned int ) ) + { + } + + I Insert( const char *pName, const T &element ) + { + extern const char *ResponseCopyString( const char *in ); + char const *pString = ResponseCopyString( pName ); + unsigned int hash = RR_HASH( pString ); + m_ReverseMap.Insert( hash, pString ); + return CUtlMap::Insert( hash, element ); + } + + I Insert( const char *pName ) + { + extern const char *ResponseCopyString( const char *in ); + char const *pString = ResponseCopyString( pName ); + unsigned int hash = RR_HASH( pString ); + m_ReverseMap.Insert( hash, pString ); + return CUtlMap::Insert( hash ); + } + + I Find( char const *pName ) const + { + unsigned int hash = RR_HASH( pName ); + return CUtlMap::Find( hash ); + } + + const char *GetElementName( I i ) + { + int k = this->Key( i ); + int slot = m_ReverseMap.Find( k ); + if ( slot == m_ReverseMap.InvalidIndex() ) + return ""; + return m_ReverseMap[ slot ]; + } + + const char *GetElementName( I i ) const + { + int k = this->Key( i ); + int slot = m_ReverseMap.Find( k ); + if ( slot == m_ReverseMap.InvalidIndex() ) + return ""; + return m_ReverseMap[ slot ]; + } + + private: + CUtlMap< unsigned int, const char * > m_ReverseMap; + + }; + + // define this to 1 to enable printing some occupancy + // information on the response system via concommmand + // rr_dumphashinfo + #define RR_DUMPHASHINFO_ENABLED 0 + // The Rules are partitioned based on a variety of factors (presently, + // speaker and concept) for faster lookup, basically a seperate-chained hash. + struct ResponseRulePartition + { + ResponseRulePartition( void ); + ~ResponseRulePartition(); + + typedef CResponseDict< Rule * > tRuleDict; + typedef uint32 tIndex; // an integer that can be used to find any rule in the dict + + /// get the appropriate m_rules dict for the provided rule + tRuleDict &GetDictForRule( CResponseSystem *pSystem, Rule *pRule ); + + /// get all bucket full of rules that might possibly match the given criteria. + /// (right now they are bucketed such that all rules that can possibly match a + /// criteria are in one of two dictionaries) + void GetDictsForCriteria( CUtlVectorFixed< ResponseRulePartition::tRuleDict *, 2 > *pResult, const CriteriaSet &criteria ); + + // dump everything. + void RemoveAll(); +#ifdef MAPBASE + void PurgeAndDeleteElements(); +#endif + + inline Rule &operator[]( tIndex idx ); + int Count( void ); // number of elements inside, but you can't iterate from 0 to this + char const *GetElementName( const tIndex &i ) const; + Rule *FindByName( char const *name ) const; + + /// given a dictionary and an element number inside that dict, + /// return a tIndex + tIndex IndexFromDictElem( tRuleDict* pDict, int elem ); + + // for iteration: + inline tIndex First( void ); + inline tIndex Next( const tIndex &idx ); + inline bool IsValid( const tIndex &idx ) const; + inline static tIndex InvalidIdx( void ) + { + return ((tIndex) -1); + } + + // used only for debug prints, do not rely on them otherwise + inline unsigned int BucketFromIdx( const tIndex &idx ) const ; + inline unsigned int PartFromIdx( const tIndex &idx ) const ; + + enum { + N_RESPONSE_PARTITIONS = 256, + kIDX_ELEM_MASK = 0xFFF, ///< this is used to mask the element number part of a ResponseRulePartition::tIndex + }; + +#if RR_DUMPHASHINFO_ENABLED + void PrintBucketInfo( CResponseSystem *pSys ); +#endif + + private: + tRuleDict m_RuleParts[N_RESPONSE_PARTITIONS]; + unsigned int GetBucketForSpeakerAndConcept( const char *pszSpeaker, const char *pszConcept, const char *pszSubject ); + }; + + // // // // // inline functions + + inline ResponseRulePartition::tIndex ResponseRulePartition::First( void ) + { + // find the first bucket that has anything + for ( int bucket = 0 ; bucket < N_RESPONSE_PARTITIONS; bucket++ ) + { + if ( m_RuleParts[bucket].Count() > 0 ) + return bucket << 16; + } + return InvalidIdx(); + } + + inline ResponseRulePartition::tIndex ResponseRulePartition::Next( const tIndex &idx ) + { + int bucket = BucketFromIdx( idx ); + unsigned int elem = PartFromIdx( idx ); + Assert( IsValid(idx) ); + AssertMsg( elem < kIDX_ELEM_MASK, "Too many response rules! Overflow! Doom!" ); + if ( elem + 1 < m_RuleParts[bucket].Count() ) + { + return idx+1; + } + else + { + // walk through the other buckets, skipping empty ones, until we find one with responses and give up. + while ( ++bucket < N_RESPONSE_PARTITIONS ) + { + if ( m_RuleParts[bucket].Count() > 0 ) + { + // 0th element in nth bucket + return bucket << 16; + } + } + + // out of buckets + return InvalidIdx(); + + } + } + + inline Rule &ResponseRulePartition::operator[]( tIndex idx ) + { + Assert( IsValid(idx) ); + return *m_RuleParts[ BucketFromIdx(idx) ][ PartFromIdx(idx) ] ; + } + + inline unsigned int ResponseRulePartition::BucketFromIdx( const tIndex &idx ) const + { + return idx >> 16; + } + + inline unsigned int ResponseRulePartition::PartFromIdx( const tIndex &idx ) const + { + return idx & kIDX_ELEM_MASK; + } + + inline bool ResponseRulePartition::IsValid( const tIndex & idx ) const + { + // make sure that the idx type for the dicts is still short + COMPILE_TIME_ASSERT( sizeof(m_RuleParts[0].FirstInorder()) == 2 ); + + if ( idx == -1 ) + return false; + + int bucket = idx >> 16; + unsigned int elem = idx & kIDX_ELEM_MASK; + + return ( bucket < N_RESPONSE_PARTITIONS && + elem < m_RuleParts[bucket].Count() ); + } + + //----------------------------------------------------------------------------- + // PARSER TYPES -- these are internal to the response system, and represent + // the objects as loaded from disk. + //----------------------------------------------------------------------------- + + +} + +#include "response_system.h" + +#endif \ No newline at end of file diff --git a/responserules/runtime/rr_convars.cpp b/responserules/runtime/rr_convars.cpp new file mode 100644 index 000000000..986ae0cd4 --- /dev/null +++ b/responserules/runtime/rr_convars.cpp @@ -0,0 +1,14 @@ +//========= Copyright © 1996-2010, Valve Corporation, All rights reserved. ============// +// +// Purpose: Convars used by the response rule system. +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" +#include + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + diff --git a/responserules/runtime/rr_response.cpp b/responserules/runtime/rr_response.cpp new file mode 100644 index 000000000..b652e8b37 --- /dev/null +++ b/responserules/runtime/rr_response.cpp @@ -0,0 +1,387 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include "rrbase.h" + +#include +#include "tier1/mapbase_con_groups.h" + +/* +#include "AI_Criteria.h" +#include "ai_speech.h" +#include +#include "engine/IEngineSound.h" +*/ + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace ResponseRules; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRR_Response::CRR_Response() : m_fMatchScore(0) +{ + m_Type = ResponseRules::RESPONSE_NONE; + m_szResponseName[0] = 0; + m_szMatchingRule[0]=0; + m_szContext = NULL; +#ifdef MAPBASE + m_iContextFlags = 0; +#else + m_bApplyContextToWorld = false; +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CRR_Response::CRR_Response( const CRR_Response &from ) : m_fMatchScore(0) +{ + // Assert( (void*)(&m_Type) == (void*)this ); + Invalidate(); + memcpy( this, &from, sizeof(*this) ); + m_szContext = NULL; + SetContext( from.m_szContext ); +#ifdef MAPBASE + m_iContextFlags = from.m_iContextFlags; +#else + m_bApplyContextToWorld = from.m_bApplyContextToWorld; +#endif +} + + +//----------------------------------------------------------------------------- +CRR_Response &CRR_Response::operator=( const CRR_Response &from ) +{ + // Assert( (void*)(&m_Type) == (void*)this ); + Invalidate(); + memcpy( this, &from, sizeof(*this) ); + m_szContext = NULL; + SetContext( from.m_szContext ); +#ifdef MAPBASE + m_iContextFlags = from.m_iContextFlags; +#else + m_bApplyContextToWorld = from.m_bApplyContextToWorld; +#endif + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRR_Response::~CRR_Response() +{ + if (m_szContext) + delete[] m_szContext; +} + +void CRR_Response::Invalidate() +{ + if (m_szContext) + { + delete[] m_szContext; + m_szContext = NULL; + } + m_Type = ResponseRules::RESPONSE_NONE; + m_szResponseName[0] = 0; + // not really necessary: + /* + m_szMatchingRule[0]=0; + m_szContext = NULL; + m_bApplyContextToWorld = false; + */ +} + +// please do not new or delete CRR_Responses. +void CRR_Response::operator delete(void* p) +{ + AssertMsg(false, "DO NOT new or delete CRR_Response s."); + free(p); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *response - +// *criteria - +//----------------------------------------------------------------------------- +void CRR_Response::Init( ResponseType_t type, const char *responseName, const ResponseParams& responseparams, const char *ruleName, const char *applyContext, bool bApplyContextToWorld ) +{ + m_Type = type; + Q_strncpy( m_szResponseName, responseName, sizeof( m_szResponseName ) ); + // Copy underlying criteria + Q_strncpy( m_szMatchingRule, ruleName ? ruleName : "NULL", sizeof( m_szMatchingRule ) ); + m_Params = responseparams; + SetContext( applyContext ); +#ifdef MAPBASE + bApplyContextToWorld ? m_iContextFlags = APPLYCONTEXT_WORLD : m_iContextFlags = 0; +#else + m_bApplyContextToWorld = bApplyContextToWorld; +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : *response - +// *criteria - +//----------------------------------------------------------------------------- +void CRR_Response::Init( ResponseType_t type, const char *responseName, const ResponseParams& responseparams, const char *ruleName, const char *applyContext, int iContextFlags ) +{ + m_Type = type; + Q_strncpy( m_szResponseName, responseName, sizeof( m_szResponseName ) ); + // Copy underlying criteria + Q_strncpy( m_szMatchingRule, ruleName ? ruleName : "NULL", sizeof( m_szMatchingRule ) ); + m_Params = responseparams; + SetContext( applyContext ); + m_iContextFlags = iContextFlags; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Debug-print the response. You can optionally pass in the criteria +// used to come up with this response (usually present in the calling function) +// if you want to print that as well. DO NOT store the entire criteria set in +// CRR_Response just to make this debug print cleaner. +//----------------------------------------------------------------------------- +void CRR_Response::Describe( const CriteriaSet *pDebugCriteria ) +{ + if ( pDebugCriteria ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Search criteria:\n" ); + pDebugCriteria->Describe(); + } + + if ( m_szMatchingRule[ 0 ] ) + { + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Matched rule '%s', ", m_szMatchingRule ); + } + if ( m_szContext ) + { +#ifdef MAPBASE + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "Contexts to set '%s' on ", m_szContext ); + if (m_iContextFlags & APPLYCONTEXT_WORLD) + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "world, " ); + else if (m_iContextFlags & APPLYCONTEXT_SQUAD) + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "squad, " ); + else if (m_iContextFlags & APPLYCONTEXT_ENEMY) + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "enemy, " ); + else + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "speaker, " ); +#else + DevMsg( "Contexts to set '%s' on %s, ", m_szContext, m_bApplyContextToWorld ? "world" : "speaker" ); +#endif + } + + CGMsg( 1, CON_GROUP_RESPONSE_SYSTEM, "response %s = '%s'\n", DescribeResponse( (ResponseType_t)m_Type ), m_szResponseName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CRR_Response::GetName( char *buf, size_t buflen ) const +{ + Q_strncpy( buf, m_szResponseName, buflen ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CRR_Response::GetResponse( char *buf, size_t buflen ) const +{ + GetName( buf, buflen ); +} + + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CRR_Response::GetRule( char *buf, size_t buflen ) const +{ + Q_strncpy( buf, m_szMatchingRule, buflen ); +} +#endif + + +const char* ResponseRules::CRR_Response::GetNamePtr() const +{ + return m_szResponseName; +} +const char* ResponseRules::CRR_Response::GetResponsePtr() const +{ + return m_szResponseName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : char const +//----------------------------------------------------------------------------- +const char *CRR_Response::DescribeResponse( ResponseType_t type ) +{ + if ( (int)type < 0 || (int)type >= ResponseRules::NUM_RESPONSES ) + { + Assert( 0 ); + return "???CRR_Response bogus index"; + } + + switch( type ) + { + default: + { + Assert( 0 ); + } + // Fall through + case ResponseRules::RESPONSE_NONE: + return "RESPONSE_NONE"; + case ResponseRules::RESPONSE_SPEAK: + return "RESPONSE_SPEAK"; + case ResponseRules::RESPONSE_SENTENCE: + return "RESPONSE_SENTENCE"; + case ResponseRules::RESPONSE_SCENE: + return "RESPONSE_SCENE"; + case ResponseRules::RESPONSE_RESPONSE: + return "RESPONSE_RESPONSE"; + case ResponseRules::RESPONSE_PRINT: + return "RESPONSE_PRINT"; + case ResponseRules::RESPONSE_ENTITYIO: + return "RESPONSE_ENTITYIO"; +#ifdef MAPBASE + case ResponseRules::RESPONSE_VSCRIPT: + return "RESPONSE_VSCRIPT"; + case ResponseRules::RESPONSE_VSCRIPT_FILE: + return "RESPONSE_VSCRIPT_FILE"; +#endif + } + + return "RESPONSE_NONE"; +} + +/* +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRR_Response::Release() +{ + delete this; +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: +// Output : soundlevel_t +//----------------------------------------------------------------------------- +soundlevel_t CRR_Response::GetSoundLevel() const +{ + if ( m_Params.flags & ResponseParams::RG_SOUNDLEVEL ) + { + return (soundlevel_t)m_Params.soundlevel; + } + + return SNDLVL_TALKING; +} + +float CRR_Response::GetRespeakDelay( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_RESPEAKDELAY ) + { + interval_t temp; + m_Params.respeakdelay.ToInterval( temp ); + return RandomInterval( temp ); + } + + return 0.0f; +} + +float CRR_Response::GetWeaponDelay( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_WEAPONDELAY ) + { + interval_t temp; + m_Params.weapondelay.ToInterval( temp ); + return RandomInterval( temp ); + } + + return 0.0f; +} + +bool CRR_Response::GetSpeakOnce( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_SPEAKONCE ) + { + return true; + } + + return false; +} + +bool CRR_Response::ShouldntUseScene( void ) const +{ + return ( m_Params.flags & ResponseParams::RG_DONT_USE_SCENE ) != 0; +} + +bool CRR_Response::ShouldBreakOnNonIdle( void ) const +{ + return ( m_Params.flags & ResponseParams::RG_STOP_ON_NONIDLE ) != 0; +} + +int CRR_Response::GetOdds( void ) const +{ + if ( m_Params.flags & ResponseParams::RG_ODDS ) + { + return m_Params.odds; + } + return 100; +} + +float CRR_Response::GetDelay() const +{ + if ( m_Params.flags & ResponseParams::RG_DELAYAFTERSPEAK ) + { + interval_t temp; + m_Params.delay.ToInterval( temp ); + return RandomInterval( temp ); + } + return 0.0f; +} + +float CRR_Response::GetPreDelay() const +{ + if ( m_Params.flags & ResponseParams::RG_DELAYBEFORESPEAK ) + { + interval_t temp; + m_Params.predelay.ToInterval( temp ); + return RandomInterval( temp ); + } + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets context string +// Output : void +//----------------------------------------------------------------------------- +void CRR_Response::SetContext( const char *context ) +{ + if (m_szContext) + { + delete[] m_szContext; + m_szContext = NULL; + } + + if ( context ) + { + int len = Q_strlen( context ); + m_szContext = new char[ len + 1 ]; + Q_memcpy( m_szContext, context, len ); + m_szContext[ len ] = 0; + } +} diff --git a/responserules/runtime/rr_speechconcept.cpp b/responserules/runtime/rr_speechconcept.cpp new file mode 100644 index 000000000..7e1e04abf --- /dev/null +++ b/responserules/runtime/rr_speechconcept.cpp @@ -0,0 +1,73 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "rrbase.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#if RR_CONCEPTS_ARE_STRINGS +#pragma error("RR_CONCEPTS_ARE_STRINGS no longer supported") +#else + +using namespace ResponseRules; + +// Used to turn ad-hoc concept from strings into numbers. +CRR_ConceptSymbolTable *g_pRRConceptTable = NULL; + +// Q&D hack to defer initialization of concept table until I can figure out where it +// really needs to come from. +static void InitializeRRConceptTable() +{ + if (g_pRRConceptTable == NULL) + { + g_pRRConceptTable = new CRR_ConceptSymbolTable( 64, 64, true ); + } +} + +// construct from string +CRR_Concept::CRR_Concept(const char *fromString) +{ + InitializeRRConceptTable(); + m_iConcept = g_pRRConceptTable->AddString(fromString); +} + +CRR_Concept &CRR_Concept::operator=(const char *fromString) +{ + InitializeRRConceptTable(); + m_iConcept = g_pRRConceptTable->AddString(fromString); + return *this; +} + +bool CRR_Concept::operator==(const char *pszConcept) +{ + int otherConcept = g_pRRConceptTable->Find(pszConcept); + return ( otherConcept != UTL_INVAL_SYMBOL && otherConcept == m_iConcept ); +} + +const char *CRR_Concept::GetStringConcept() const +{ + InitializeRRConceptTable(); + AssertMsg( m_iConcept.IsValid(), "AI Concept has invalid string symbol.\n" ); + const char * retval = g_pRRConceptTable->String(m_iConcept); + AssertMsg( retval, "An RR_Concept couldn't find its string in the symbol table!\n" ); + if (retval == NULL) + { + Warning( "An RR_Concept couldn't find its string in the symbol table!\n" ); + retval = ""; + } + return retval; +} + +const char *CRR_Concept::GetStringForGenericId(tGenericId genericId) +{ + InitializeRRConceptTable(); + return g_pRRConceptTable->String(genericId); +} + +#endif diff --git a/responserules/runtime/rrbase.h b/responserules/runtime/rrbase.h new file mode 100644 index 000000000..e7af74dee --- /dev/null +++ b/responserules/runtime/rrbase.h @@ -0,0 +1,59 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RRBASE_H +#define RRBASE_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _WIN32 +// Silence certain warnings +// #pragma warning(disable : 4244) // int or float down-conversion +// #pragma warning(disable : 4305) // int or float data truncation +// #pragma warning(disable : 4201) // nameless struct/union +// #pragma warning(disable : 4511) // copy constructor could not be generated +// #pragma warning(disable : 4675) // resolved overload was found by argument dependent lookup +#endif + +#ifdef _DEBUG +#define DEBUG 1 +#endif + +// Misc C-runtime library headers +#include +#include +#include + +// tier 0 +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "basetypes.h" + +// tier 1 +#include "tier1/strtools.h" +#include "utlvector.h" +#include "utlsymbol.h" + +// tier 2 +#include "string_t.h" + +// Shared engine/DLL constants +#include "const.h" +#include "edict.h" + +// app +#if defined(_X360) +#define DISABLE_DEBUG_HISTORY 1 +#endif + +#include "responserules/response_types.h" +#include "response_types_internal.h" +#include "responserules/response_host_interface.h" + + +#endif // CBASE_H diff --git a/responserules/runtime/rrrlib.cpp b/responserules/runtime/rrrlib.cpp new file mode 100644 index 000000000..0d2c49fd7 --- /dev/null +++ b/responserules/runtime/rrrlib.cpp @@ -0,0 +1,13 @@ +/// PLACEHOLDER FILE FOR RESPONSE RULES RUNTIME LIBRARY + +#include "rrbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +namespace ResponseRules +{ + /// Custom symbol table for the response rules. + CUtlSymbolTable g_RS; +}; \ No newline at end of file diff --git a/responserules/runtime/stdafx.cpp b/responserules/runtime/stdafx.cpp new file mode 100644 index 000000000..4199359f2 --- /dev/null +++ b/responserules/runtime/stdafx.cpp @@ -0,0 +1,11 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Builds the precompiled header for the game DLL +// +// $NoKeywords: $ +//=============================================================================// + + +#include "rrbase.h" + +// NOTE: DO NOT ADD ANY CODE OR HEADERS TO THIS FILE!!! diff --git a/scripts/waifulib/squirrel.py b/scripts/waifulib/squirrel.py new file mode 100644 index 000000000..eb26e9714 --- /dev/null +++ b/scripts/waifulib/squirrel.py @@ -0,0 +1,31 @@ +# encoding: utf-8 +# squirrel.py -- nut file support + +from waflib import Errors, Logs, Task +from waflib.TaskGen import extension + +@extension('nut') +def squirrel_hook(self, node): + "Binds Squirrel file extensions to create :py:class:`waflib.Tools.Squirrel.cxx` instances" + return self.create_compiled_task('nut', node) + +class squirrel(Task.Task): + """ + Squirrel support. + + Compile *.nut* files into *.cnut*:: + + def configure(conf): + conf.load('squirrel') + conf.env.SQUIRRELDIR = '/usr/local/share/myapp/scripts/' + def build(bld): + bld(source='foo.nut') + """ + color = 'BROWN' + run_str = '${SQUIRREL} ${SRC}' + ext_out = ['.cnut'] + +def configure(conf): + # TODO(halotroop2288): This is a WIP. I've never dealt with Squirrel before, + # never used Python, and I'm brand new to using Waf. So this is going to take some time. + return diff --git a/tier1/KeyValues.cpp b/tier1/KeyValues.cpp index 75664e435..a700c7447 100644 --- a/tier1/KeyValues.cpp +++ b/tier1/KeyValues.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -30,6 +30,9 @@ #include "utlqueue.h" #include "UtlSortVector.h" #include "convar.h" +#ifdef MAPBASE +#include "icommandline.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include @@ -216,19 +219,19 @@ static CLeakTrack track; #else -#define TRACK_KV_ADD( ptr, name ) -#define TRACK_KV_REMOVE( ptr ) +#define TRACK_KV_ADD( ptr, name ) +#define TRACK_KV_REMOVE( ptr ) #endif //----------------------------------------------------------------------------- -// Purpose: An arbitrarily growable string table for KeyValues key names. +// Purpose: An arbitrarily growable string table for KeyValues key names. // See the comment in the header for more info. //----------------------------------------------------------------------------- class CKeyValuesGrowableStringTable { -public: +public: // Constructor CKeyValuesGrowableStringTable() : #ifdef PLATFORM_64BITS @@ -279,7 +282,7 @@ class CKeyValuesGrowableStringTable } private: - + // A class plugged into CUtlHash that allows us to change the behavior of the table // and store only the index in the table. class CLookupFunctor @@ -296,7 +299,7 @@ class CKeyValuesGrowableStringTable { const char *pchLhs = nLhs > 0 ? m_pchCurBase + nLhs : m_pchCurString; const char *pchRhs = nRhs > 0 ? m_pchCurBase + nRhs : m_pchCurString; - + return ( 0 == V_stricmp( pchLhs, pchRhs ) ); } @@ -458,7 +461,7 @@ void KeyValues::Init() m_sValue = NULL; m_wsValue = NULL; m_pValue = NULL; - + m_bHasEscapeSequences = false; m_bEvaluateConditionals = true; @@ -504,8 +507,8 @@ void KeyValues::RemoveEverything() } //----------------------------------------------------------------------------- -// Purpose: -// Input : *f - +// Purpose: +// Input : *f - //----------------------------------------------------------------------------- void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/ ) @@ -541,7 +544,7 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon wasConditional = false; if ( !buf.IsValid() ) - return NULL; + return NULL; // eating white spaces and remarks loop while ( true ) @@ -563,7 +566,7 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon if ( *c == '\"' ) { wasQuoted = true; - buf.GetDelimitedString( m_bHasEscapeSequences ? GetCStringCharConversion() : GetNoEscCharConversion(), + buf.GetDelimitedString( m_bHasEscapeSequences ? GetCStringCharConversion() : GetNoEscCharConversion(), s_pTokenBuf, KEYVALUES_TOKEN_SIZE ); return s_pTokenBuf; } @@ -620,7 +623,7 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon } #pragma warning (default:4706) - + //----------------------------------------------------------------------------- // Purpose: if parser should translate escape sequences ( /n, /t etc), set to true @@ -685,7 +688,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN #endif // If pathID is null, we cannot cache the result because that has a weird iterate-through-a-bunch-of-locations behavior. - const bool bUseCacheForRead = bUseCache && !refreshCache && pathID != NULL; + const bool bUseCacheForRead = bUseCache && !refreshCache && pathID != NULL; const bool bUseCacheForWrite = bUseCache && pathID != NULL; COM_TimestampedLog( "KeyValues::LoadFromFile(%s%s%s): Begin", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "" ); @@ -711,7 +714,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize ); Assert( buffer ); - + // read into local buffer bool bRetOK = ( ((IFileSystem *)filesystem)->ReadEx( buffer, bufSize, fileSize, f ) != 0 ); @@ -723,9 +726,9 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN buffer[fileSize+1] = 0; // double NULL terminating in case this is a unicode file bRetOK = LoadFromBuffer( resourceName, buffer, filesystem ); } - + // The cache relies on the KeyValuesSystem string table, which will only be valid if we're - // using classic mode. + // using classic mode. if ( bUseCacheForWrite && bRetOK ) { KeyValuesSystem()->AddFileKeyValuesToCache( this, resourceName, pathID ); @@ -799,7 +802,7 @@ void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t } convertedString[j] = pszString[i]; j++; - } + } INTERNALWRITE(convertedString, Q_strlen(convertedString)); } @@ -827,7 +830,7 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f // write header WriteIndents( filesystem, f, pBuf, indentLevel ); INTERNALWRITE("\"", 1); - WriteConvertedString(filesystem, f, pBuf, GetName()); + WriteConvertedString(filesystem, f, pBuf, GetName()); INTERNALWRITE("\"\n", 2); WriteIndents( filesystem, f, pBuf, indentLevel ); INTERNALWRITE("{\n", 2); @@ -842,7 +845,7 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f vecSortedKeys.InsertNoSort(dat); } vecSortedKeys.RedoSort(); - + FOR_EACH_VEC( vecSortedKeys, i ) { SaveKeyToFile( vecSortedKeys[i], filesystem, f, pBuf, indentLevel, sortKeys, bAllowEmptyString ); @@ -877,10 +880,10 @@ void KeyValues::SaveKeyToFile( KeyValues *dat, IBaseFileSystem *filesystem, File { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); - WriteConvertedString(filesystem, f, pBuf, dat->GetName()); + WriteConvertedString(filesystem, f, pBuf, dat->GetName()); INTERNALWRITE("\"\t\t\"", 4); - WriteConvertedString(filesystem, f, pBuf, dat->m_sValue); + WriteConvertedString(filesystem, f, pBuf, dat->m_sValue); INTERNALWRITE("\"\n", 2); } @@ -983,7 +986,7 @@ KeyValues *KeyValues::FindKey(intp keySymbol) const //----------------------------------------------------------------------------- // Purpose: Find a keyValue, create it if it is not found. -// Set bCreate to true to create the key if it doesn't already exist +// Set bCreate to true to create the key if it doesn't already exist // (which ensures a valid pointer will be returned) //----------------------------------------------------------------------------- KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) @@ -1066,7 +1069,7 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) return NULL; } } - + // if we've still got a subStr we need to keep looking deeper in the tree if ( subStr ) { @@ -1078,8 +1081,8 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) } //----------------------------------------------------------------------------- -// Purpose: Create a new key, with an autogenerated name. -// Name is guaranteed to be an integer, of value 1 higher than the highest +// Purpose: Create a new key, with an autogenerated name. +// Name is guaranteed to be an integer, of value 1 higher than the highest // other integer key name //----------------------------------------------------------------------------- KeyValues *KeyValues::CreateNewKey() @@ -1124,7 +1127,7 @@ KeyValues* KeyValues::CreateKeyUsingKnownLastChild( const char *keyName, KeyValu dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does dat->UsesConditionals( m_bEvaluateConditionals != 0 ); - + // add into subkey list AddSubkeyUsingKnownLastChild( dat, pLastChild ); @@ -1191,7 +1194,7 @@ void KeyValues::AddSubKey( KeyValues *pSubkey ) } - + //----------------------------------------------------------------------------- // Purpose: Remove a subkey from the list //----------------------------------------------------------------------------- @@ -1216,7 +1219,7 @@ void KeyValues::RemoveSubKey(KeyValues *subKey) kv->m_pPeer = subKey->m_pPeer; break; } - + kv = kv->m_pPeer; } } @@ -1458,7 +1461,7 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue default: return defaultValue; }; - + return dat->m_sValue; } return defaultValue; @@ -1514,7 +1517,7 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul default: return defaultValue; }; - + return (const wchar_t* )dat->m_wsValue; } return defaultValue; @@ -1531,7 +1534,7 @@ bool KeyValues::GetBool( const char *keyName, bool defaultValue, bool* optGotDef (*optGotDefault) = false; return 0 != GetInt( keyName, 0 ); } - + if ( optGotDefault ) (*optGotDefault) = true; @@ -1616,7 +1619,7 @@ void KeyValues::SetStringValue( char const *strValue ) } //----------------------------------------------------------------------------- -// Purpose: Set the string value of a keyName. +// Purpose: Set the string value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetString( const char *keyName, const char *value ) { @@ -1651,7 +1654,7 @@ void KeyValues::SetString( const char *keyName, const char *value ) } //----------------------------------------------------------------------------- -// Purpose: Set the string value of a keyName. +// Purpose: Set the string value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetWString( const char *keyName, const wchar_t *value ) { @@ -1680,7 +1683,7 @@ void KeyValues::SetWString( const char *keyName, const wchar_t *value ) } //----------------------------------------------------------------------------- -// Purpose: Set the integer value of a keyName. +// Purpose: Set the integer value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetInt( const char *keyName, int value ) { @@ -1694,7 +1697,7 @@ void KeyValues::SetInt( const char *keyName, int value ) } //----------------------------------------------------------------------------- -// Purpose: Set the integer value of a keyName. +// Purpose: Set the integer value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetUint64( const char *keyName, uint64 value ) { @@ -1715,7 +1718,7 @@ void KeyValues::SetUint64( const char *keyName, uint64 value ) } //----------------------------------------------------------------------------- -// Purpose: Set the float value of a keyName. +// Purpose: Set the float value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetFloat( const char *keyName, float value ) { @@ -1734,7 +1737,7 @@ void KeyValues::SetName( const char * setName ) } //----------------------------------------------------------------------------- -// Purpose: Set the pointer value of a keyName. +// Purpose: Set the pointer value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetPtr( const char *keyName, void *value ) { @@ -1753,7 +1756,7 @@ void KeyValues::SetPtr( const char *keyName, void *value ) //----------------------------------------------------------------------------- void KeyValues::CopyKeyValuesFromRecursive( const KeyValues& rootSrc ) { - // This code used to be recursive, which was more elegant. Unfortunately, it also blew the stack for large + // This code used to be recursive, which was more elegant. Unfortunately, it also blew the stack for large // KeyValues. So now we have the iterative version which is uglier but doesn't blow the stack. // This uses breadth-first traversal. @@ -1769,7 +1772,7 @@ void KeyValues::CopyKeyValuesFromRecursive( const KeyValues& rootSrc ) CUtlQueue nodeQ; nodeQ.Insert({ this, &rootSrc }); - while ( nodeQ.Count() > 0 ) + while ( nodeQ.Count() > 0 ) { CopyStruct cs = nodeQ.RemoveAtHead(); @@ -1781,13 +1784,13 @@ void KeyValues::CopyKeyValuesFromRecursive( const KeyValues& rootSrc ) // Copy the node contents cs.dst->CopyKeyValue( *cs.src, sizeof(tmp), tmp ); - // Add children to the queue to process later. + // Add children to the queue to process later. if (cs.src->m_pSub) { cs.dst->m_pSub = localDst = new KeyValues( NULL ); nodeQ.Insert({ localDst, cs.src->m_pSub }); } - // Process siblings until we hit the end of the line. + // Process siblings until we hit the end of the line. if (cs.src->m_pPeer) { cs.dst->m_pPeer = new KeyValues( NULL ); } @@ -1814,7 +1817,7 @@ void KeyValues::CopyKeyValue( const KeyValues& src, size_t tmpBufferSizeB, char* return; m_iDataType = src.m_iDataType; - + switch( src.m_iDataType ) { case TYPE_NONE: @@ -1864,7 +1867,7 @@ void KeyValues::CopyKeyValue( const KeyValues& src, size_t tmpBufferSizeB, char* m_Color[3] = src.m_Color[3]; } break; - + default: { // do nothing . .what the heck is this? @@ -1895,7 +1898,7 @@ void KeyValues::CopySubkeys( KeyValues *pParent ) const { // take a copy of the subkey KeyValues *dat = sub->MakeCopy(); - + // add into subkey list if (pPrev) { @@ -1958,7 +1961,7 @@ KeyValues *KeyValues::MakeCopy( void ) const case TYPE_PTR: newKeyValue->m_pValue = m_pValue; break; - + case TYPE_COLOR: newKeyValue->m_Color[0] = m_Color[0]; newKeyValue->m_Color[1] = m_Color[1]; @@ -1978,7 +1981,7 @@ KeyValues *KeyValues::MakeCopy( void ) const } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- KeyValues *KeyValues::MakeCopy( bool copySiblings ) const { @@ -2043,8 +2046,8 @@ void KeyValues::deleteThis() } //----------------------------------------------------------------------------- -// Purpose: -// Input : includedKeys - +// Purpose: +// Input : includedKeys - //----------------------------------------------------------------------------- void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) { @@ -2065,13 +2068,13 @@ void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) } } -void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoinclude, +void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoinclude, IBaseFileSystem* pFileSystem, const char *pPathID, CUtlVector< KeyValues * >& includedKeys ) { Assert( resourceName ); Assert( filetoinclude ); Assert( pFileSystem ); - + // Load it... if ( !pFileSystem ) { @@ -2090,8 +2093,8 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi { break; } - - if ( fullpath[ len - 1 ] == '\\' || + + if ( fullpath[ len - 1 ] == '\\' || fullpath[ len - 1 ] == '/' ) { break; @@ -2126,8 +2129,8 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi } //----------------------------------------------------------------------------- -// Purpose: -// Input : baseKeys - +// Purpose: +// Input : baseKeys - //----------------------------------------------------------------------------- void KeyValues::MergeBaseKeys( CUtlVector< KeyValues * >& baseKeys ) { @@ -2143,7 +2146,7 @@ void KeyValues::MergeBaseKeys( CUtlVector< KeyValues * >& baseKeys ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Input : baseKV - keyvalues we're basing ourselves on //----------------------------------------------------------------------------- void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV ) @@ -2166,7 +2169,7 @@ void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV ) newChild->RecursiveMergeKeyValues( baseChild ); bFoundMatch = true; break; - } + } } // If not merged, append this key @@ -2244,6 +2247,28 @@ bool EvaluateConditional( const char *str ) if ( Q_stristr( str, "$POSIX" ) ) return IsPosix() ^ bNot; +#ifdef MAPBASE + // Custom conditional + switch( str[bNot ? 1 : 0] ) + { + case '%': + { + // Look for a cvar + ConVarRef cvar( str + (bNot ? 2 : 1), true ); + if (cvar.IsValid()) + { + return cvar.GetBool() ^ bNot; + } + } break; + + case '-': + { + // Look for a command line param + return (CommandLine()->CheckParm( bNot ? str+1 : str ) != 0) ^ bNot; + } break; + } +#endif // MAPBASE + return false; } @@ -2259,8 +2284,8 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase CUtlVector< KeyValues * > baseKeys; bool wasQuoted; bool wasConditional; - g_KeyValuesErrorStack.SetFilename( resourceName ); - do + g_KeyValuesErrorStack.SetFilename( resourceName ); + do { bool bAccepted = true; @@ -2378,7 +2403,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase } } - g_KeyValuesErrorStack.SetFilename( "" ); + g_KeyValuesErrorStack.SetFilename( "" ); return true; } @@ -2414,7 +2439,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, I } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf ) { @@ -2480,7 +2505,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got NULL key" ); break; } - + if ( *value == '}' && !wasQuoted ) { g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got } in key" ); @@ -2494,14 +2519,14 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b // sub value list dat->RecursiveLoadFromBuffer( resourceName, buf ); } - else + else { if ( wasConditional ) { g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got conditional between key and value" ); break; } - + if (dat->m_sValue) { delete[] dat->m_sValue; @@ -2527,10 +2552,10 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b pFEnd = (char *)value; } #endif - + if ( *value == 0 ) { - dat->m_iDataType = TYPE_STRING; + dat->m_iDataType = TYPE_STRING; } else if ( ( 18 == len ) && ( value[0] == '0' ) && ( value[1] == 'x' ) ) { @@ -2539,7 +2564,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b for( int i=2; i < 2 + 16; i++ ) { char digit = value[i]; - if ( digit >= 'a' ) + if ( digit >= 'a' ) digit -= 'a' - ( '9' + 1 ); else if ( digit >= 'A' ) @@ -2552,12 +2577,12 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b } else if ( (pFEnd > pIEnd) && (pFEnd == pSEnd) ) { - dat->m_flValue = fval; + dat->m_flValue = fval; dat->m_iDataType = TYPE_FLOAT; } else if (pIEnd == pSEnd && !bOverflow) { - dat->m_iValue = ival; + dat->m_iValue = ival; dat->m_iDataType = TYPE_INT; } else @@ -2623,7 +2648,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) return false; // Write subkeys: - + // loop through all our peers for ( KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer ) { @@ -2661,7 +2686,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) case TYPE_INT: { - buffer.PutInt( dat->m_iValue ); + buffer.PutInt( dat->m_iValue ); break; } @@ -2695,7 +2720,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) } // write tail, marks end of peers - buffer.PutUnsignedChar( TYPE_NUMTYPES ); + buffer.PutUnsignedChar( TYPE_NUMTYPES ); return buffer.IsValid(); } @@ -2711,7 +2736,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) RemoveEverything(); // remove current content Init(); // reset - + if ( nStackDepth > 100 ) { AssertMsgOnce( false, "KeyValues::ReadAsBinary() stack depth > 100\n" ); @@ -2720,7 +2745,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) KeyValues *dat = this; types_t type = (types_t)buffer.GetUnsignedChar(); - + // loop through all our peers while ( true ) { @@ -2753,7 +2778,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) int len = Q_strlen( token ); dat->m_sValue = new char[len + 1]; Q_memcpy( dat->m_sValue, token, len+1 ); - + break; } case TYPE_WSTRING: @@ -2970,12 +2995,12 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl //----------------------------------------------------------------------------- // Helper function for processing a keyvalue tree for console resolution support. -// Alters key/values for easier console video resolution support. +// Alters key/values for easier console video resolution support. // If running SD (640x480), the presence of "???_lodef" creates or slams "???". // If running HD (1280x720), the presence of "???_hidef" creates or slams "???". //----------------------------------------------------------------------------- bool KeyValues::ProcessResolutionKeys( const char *pResString ) -{ +{ if ( !pResString ) { // not for pc, console only @@ -3009,7 +3034,7 @@ bool KeyValues::ProcessResolutionKeys( const char *pResString ) // find and delete the original key (if any) KeyValues *pKey = FindKey( normalKeyName ); if ( pKey ) - { + { // remove the key RemoveSubKey( pKey ); } @@ -3036,7 +3061,7 @@ bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */, if ( bSorted ) { CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedKeys; - + // Dump values for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() ) { @@ -3049,7 +3074,7 @@ bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */, if ( !pDump->KvWriteValue( vecSortedKeys[i], nIndentLevel + 1 ) ) return false; } - + vecSortedKeys.Purge(); // Dump subkeys @@ -3140,7 +3165,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel return false; } break; - + case KeyValues::TYPE_FLOAT: { float fl = val->GetFloat(); diff --git a/tier1/bitbuf.cpp b/tier1/bitbuf.cpp index 26d4d5759..c88b8c09b 100644 --- a/tier1/bitbuf.cpp +++ b/tier1/bitbuf.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -85,7 +85,7 @@ void SetBitBufErrorHandler( BitBufErrorHandler fn ) uint32 g_LittleBits[32]; -// Precalculated bit masks for WriteUBitLong. Using these tables instead of +// Precalculated bit masks for WriteUBitLong. Using these tables instead of // doing the calculations gives a 33% speedup in WriteUBitLong. uint32 g_BitWriteMasks[32][33]; @@ -114,7 +114,7 @@ class CBitWriteMasksInit for ( unsigned int littleBit=0; littleBit < 32; littleBit++ ) StoreLittleDWord( &g_LittleBits[littleBit], 0, 1u<> 31 ) & ~nPreserveBits; nValue &= nPreserveBits; nValue |= nSignExtension; - + AssertMsg2( nValue == data, "WriteSBitLong: 0x%08x does not fit in %d bits", data, numbits ); WriteUBitLong( nValue, numbits, false ); @@ -273,7 +273,7 @@ void bf_write::WriteVarInt32( uint32 data ) } else // Slow path { - while ( data > 0x7F ) + while ( data > 0x7F ) { WriteUBitLong( (data & 0x7F) | 0x80, 8 ); data >>= 7; @@ -387,7 +387,7 @@ void bf_write::WriteVarInt64( uint64 data ) } else // slow path { - while ( data > 0x7F ) + while ( data > 0x7F ) { WriteUBitLong( (data & 0x7F) | 0x80, 8 ); data >>= 7; @@ -469,13 +469,13 @@ bool bf_write::WriteBits(const void *pInData, int nBits) ++pOut; nBitsLeft -= 8; } - + if ( IsPC() && (nBitsLeft >= 32) && (m_iCurBit & 7) == 0 ) { // current bit is byte aligned, do block copy - int numbytes = nBitsLeft >> 3; + int numbytes = nBitsLeft >> 3; int numbits = numbytes << 3; - + Q_memcpy( (char*)m_pData+(m_iCurBit>>3), pOut, numbytes ); pOut += numbytes; nBitsLeft -= numbits; @@ -501,7 +501,7 @@ bool bf_write::WriteBits(const void *pInData, int nBits) *pData &= bitMaskLeft; *pData |= curData << iBitsRight; - pData++; + pData++; if ( iBitsLeft < 32 ) { @@ -523,7 +523,7 @@ bool bf_write::WriteBits(const void *pInData, int nBits) ++pOut; nBitsLeft -= 8; } - + // write remaining bits if ( nBitsLeft ) { @@ -570,7 +570,7 @@ void bf_write::WriteBitCoordMP( const float f, bool bIntegral, bool bLowPrecisio #endif int signbit = (f <= -( bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION )); int intval = (int)abs(f); - int fractval = bLowPrecision ? + int fractval = bLowPrecision ? ( abs((int)(f*COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION-1) ) : ( abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1) ); @@ -642,7 +642,7 @@ void bf_write::WriteBitCoord (const float f) intval--; WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); } - + // Send the fraction if we have one if ( fractval ) { @@ -703,7 +703,7 @@ void bf_write::WriteBitVec3Normal( const Vector& fa ) WriteBitNormal( fa[0] ); if ( yflag ) WriteBitNormal( fa[1] ); - + // Write z sign bit int signbit = (fa[2] <= -NORMAL_RESOLUTION); WriteOneBit( signbit ); @@ -870,7 +870,7 @@ unsigned int bf_read::CheckReadUBitLong(int numbits) r |= nBitValue << i; } m_iCurBit -= numbits; - + return r; } @@ -883,7 +883,7 @@ void bf_read::ReadBits(void *pOutData, int nBits) unsigned char *pOut = (unsigned char*)pOutData; int nBitsLeft = nBits; - + // align output to dword boundary while( ((size_t)pOut & 3) != 0 && nBitsLeft >= 8 ) { @@ -911,7 +911,7 @@ void bf_read::ReadBits(void *pOutData, int nBits) ++pOut; nBitsLeft -= 8; } - + // read remaining bits if ( nBitsLeft ) { @@ -982,7 +982,7 @@ unsigned int bf_read::PeekUBitLong( int numbits ) r |= BitForBitnum(i); } } - + *this = savebf; #ifdef BIT_VERBOSE @@ -1024,9 +1024,9 @@ uint32 bf_read::ReadVarInt32() int count = 0; uint32 b; - do + do { - if ( count == bitbuf::kMaxVarint32Bytes ) + if ( count == bitbuf::kMaxVarint32Bytes ) { return result; } @@ -1044,9 +1044,9 @@ uint64 bf_read::ReadVarInt64() int count = 0; uint64 b; - do + do { - if ( count == bitbuf::kMaxVarintBytes ) + if ( count == bitbuf::kMaxVarintBytes ) { return result; } @@ -1148,7 +1148,7 @@ float bf_read::ReadBitCoordMP( bool bIntegral, bool bLowPrecision ) } return 0.f; } - + static const float mul_table[4] = { 1.f/(1<> COORD_INTEGER_BITS_MP; uint fracbits = bits >> COORD_INTEGER_BITS; @@ -1198,7 +1198,7 @@ float bf_read::ReadBitCoordMP( bool bIntegral, bool bLowPrecision ) uint intbitsLow = intpart << COORD_FRACTIONAL_BITS_MP_LOWPRECISION; uint intbits = intpart << COORD_FRACTIONAL_BITS; uint selectNotLow = (uint)bLowPrecision - 1; - + intbits -= intbitsLow; intbits &= selectNotLow; intbits += intbitsLow; @@ -1272,12 +1272,12 @@ void bf_read::ReadBitVec3Coord( Vector& fa ) { int xflag, yflag, zflag; - // This vector must be initialized! Otherwise, If any of the flags aren't set, + // This vector must be initialized! Otherwise, If any of the flags aren't set, // the corresponding component will not be read and will be stack garbage. fa.Init( 0, 0, 0 ); xflag = ReadOneBit(); - yflag = ReadOneBit(); + yflag = ReadOneBit(); zflag = ReadOneBit(); if ( xflag ) @@ -1309,7 +1309,7 @@ float bf_read::ReadBitNormal (void) void bf_read::ReadBitVec3Normal( Vector& fa ) { int xflag = ReadOneBit(); - int yflag = ReadOneBit(); + int yflag = ReadOneBit(); if (xflag) fa[0] = ReadBitNormal(); @@ -1357,13 +1357,13 @@ int64 bf_read::ReadLongLong() float bf_read::ReadFloat() { - float ret; - Assert( sizeof(ret) == 4 ); - ReadBits(&ret, 32); + float retLocl; + Assert( sizeof(retLocl) == 4 ); + ReadBits(&retLocl, 32); // Swap the float, since ReadBits reads raw data - LittleFloat( &ret, &ret ); - return ret; + LittleFloat( &retLocl, &retLocl ); + return retLocl; } bool bf_read::ReadBytes(void *pOut, int nBytes) @@ -1411,7 +1411,7 @@ bool bf_read::ReadString( char *pStr, int maxLen, bool bLine, int *pOutNumChars char* bf_read::ReadAndAllocateString( bool *pOverflow ) { char str[2048]; - + int nChars; bool bOverflow = !ReadString( str, sizeof( str ), false, &nChars ); if ( pOverflow ) @@ -1441,7 +1441,7 @@ void bf_read::ExciseBits( int startbit, int bitstoremove ) } Seek( startbit ); - + m_nDataBits -= bitstoremove; m_nDataBytes = m_nDataBits >> 3; } @@ -1475,7 +1475,7 @@ int bf_read::CompareBitsAt( int offset, bf_read * RESTRICT other, int otherOffse x ^= LoadLittleDWord( (uint32*)pData2, 1 ) << (32 - iStartBit2); if ( x != 0 ) { - return x; + return x; } ++pData1; ++pData2; diff --git a/tier1/convar.cpp b/tier1/convar.cpp index ca8e52a64..be5ba7dc8 100644 --- a/tier1/convar.cpp +++ b/tier1/convar.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -32,7 +32,7 @@ //----------------------------------------------------------------------------- -// Statically constructed list of ConCommandBases, +// Statically constructed list of ConCommandBases, // used for registering them with the ICVar interface //----------------------------------------------------------------------------- ConCommandBase *ConCommandBase::s_pConCommandBases = NULL; @@ -120,18 +120,18 @@ ConCommandBase::ConCommandBase( const char *pName, const char *pHelpString /*=0* } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- ConCommandBase::~ConCommandBase( void ) { } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool ConCommandBase::IsCommand( void ) const -{ +{ // Assert( 0 ); This can't assert. . causes a recursive assert in Sys_Printf, etc. return true; } @@ -147,11 +147,11 @@ CVarDLLIdentifier_t ConCommandBase::GetDLLIdentifier() const //----------------------------------------------------------------------------- -// Purpose: -// Input : *pName - -// callback - -// *pHelpString - -// flags - +// Purpose: +// Input : *pName - +// callback - +// *pHelpString - +// flags - //----------------------------------------------------------------------------- void ConCommandBase::CreateBase( const char *pName, const char *pHelpString /*= 0*/, int flags /*= 0*/ ) { @@ -219,8 +219,8 @@ const char *ConCommandBase::GetName( void ) const //----------------------------------------------------------------------------- -// Purpose: -// Input : flag - +// Purpose: +// Input : flag - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool ConCommandBase::IsFlagSet( int flag ) const @@ -229,8 +229,8 @@ bool ConCommandBase::IsFlagSet( int flag ) const } //----------------------------------------------------------------------------- -// Purpose: -// Input : flags - +// Purpose: +// Input : flags - //----------------------------------------------------------------------------- void ConCommandBase::AddFlags( int flags ) { @@ -243,7 +243,7 @@ void ConCommandBase::AddFlags( int flags ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Output : const ConCommandBase //----------------------------------------------------------------------------- const ConCommandBase *ConCommandBase::GetNext( void ) const @@ -259,7 +259,7 @@ ConCommandBase *ConCommandBase::GetNext( void ) //----------------------------------------------------------------------------- // Purpose: Copies string using local new/delete operators -// Input : *from - +// Input : *from - // Output : char //----------------------------------------------------------------------------- char *ConCommandBase::CopyString( const char *from ) @@ -282,7 +282,7 @@ char *ConCommandBase::CopyString( const char *from ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Output : const char //----------------------------------------------------------------------------- const char *ConCommandBase::GetHelpText( void ) const @@ -410,7 +410,7 @@ bool CCommand::Tokenize( const char *pCommand, characterset_t *pBreakSet ) memcpy( m_pArgSBuffer, pCommand, nLen + 1 ); // Parse the current command into the current command buffer - CUtlBuffer bufParse( m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY ); + CUtlBuffer bufParse( m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY ); int nArgvBufferSize = 0; while ( bufParse.IsValid() && ( m_nArgc < COMMAND_MAX_ARGC ) ) { @@ -489,7 +489,7 @@ int CCommand::FindArgInt( const char *pName, int nDefaultVal ) const //----------------------------------------------------------------------------- -// Default console command autocompletion function +// Default console command autocompletion function //----------------------------------------------------------------------------- int DefaultCompletionFunc( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ) { @@ -553,10 +553,10 @@ ConCommand::~ConCommand( void ) //----------------------------------------------------------------------------- -// Purpose: Returns true if this is a command +// Purpose: Returns true if this is a command //----------------------------------------------------------------------------- bool ConCommand::IsCommand( void ) const -{ +{ return true; } @@ -624,7 +624,7 @@ int ConCommand::AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString //----------------------------------------------------------------------------- -// Returns true if the console command can autocomplete +// Returns true if the console command can autocomplete //----------------------------------------------------------------------------- bool ConCommand::CanAutoComplete( void ) { @@ -691,7 +691,10 @@ ConVar::~ConVar( void ) //----------------------------------------------------------------------------- void ConVar::InstallChangeCallback( FnChangeCallback_t callback ) { +#ifndef MAPBASE_VSCRIPT Assert( !m_pParent->m_fnChangeCallback || !callback ); +#endif // MAPBASE_VSCRIPT + m_pParent->m_fnChangeCallback = callback; if ( m_pParent->m_fnChangeCallback ) @@ -731,17 +734,17 @@ const char *ConVar::GetName( void ) const } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool ConVar::IsCommand( void ) const -{ +{ return false; } //----------------------------------------------------------------------------- -// Purpose: -// Input : +// Purpose: +// Input : //----------------------------------------------------------------------------- void ConVar::Init() { @@ -749,8 +752,8 @@ void ConVar::Init() } //----------------------------------------------------------------------------- -// Purpose: -// Input : *value - +// Purpose: +// Input : *value - //----------------------------------------------------------------------------- void ConVar::InternalSetValue( const char *value ) { @@ -794,8 +797,8 @@ void ConVar::InternalSetValue( const char *value ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *tempVal - +// Purpose: +// Input : *tempVal - //----------------------------------------------------------------------------- void ConVar::ChangeStringValue( const char *tempVal, float flOldValue ) { @@ -803,7 +806,7 @@ void ConVar::ChangeStringValue( const char *tempVal, float flOldValue ) char* pszOldValue = (char*)stackalloc( m_StringLength ); memcpy( pszOldValue, m_pszString, m_StringLength ); - + if ( tempVal ) { int len = Q_strlen(tempVal) + 1; @@ -821,7 +824,7 @@ void ConVar::ChangeStringValue( const char *tempVal, float flOldValue ) memcpy( m_pszString, tempVal, len ); } - else + else { *m_pszString = 0; } @@ -843,7 +846,7 @@ void ConVar::ChangeStringValue( const char *tempVal, float flOldValue ) //----------------------------------------------------------------------------- // Purpose: Check whether to clamp and then perform clamp -// Input : value - +// Input : value - // Output : Returns true if value changed //----------------------------------------------------------------------------- bool ConVar::ClampValue( float& value ) @@ -890,8 +893,8 @@ bool ConVar::ClampValue( float& value ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *value - +// Purpose: +// Input : *value - //----------------------------------------------------------------------------- void ConVar::InternalSetFloatValue( float fNewValue, bool bForce /*= false */ ) { @@ -930,8 +933,8 @@ void ConVar::InternalSetFloatValue( float fNewValue, bool bForce /*= false */ ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *value - +// Purpose: +// Input : *value - //----------------------------------------------------------------------------- void ConVar::InternalSetIntValue( int nValue ) { @@ -977,7 +980,7 @@ void ConVar::InternalSetIntValue( int nValue ) //----------------------------------------------------------------------------- void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= 0*/, const char *pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/, - bool bMax /*= false*/, float fMax /*= false*/, bool bCompMin /*= false */, + bool bMax /*= false*/, float fMax /*= false*/, bool bCompMin /*= false */, float fCompMin /*= 0.0*/, bool bCompMax /*= false*/, float fCompMax /*= 0.0*/, FnChangeCallback_t callback /*= NULL*/ ) { @@ -989,7 +992,7 @@ void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= m_StringLength = V_strlen( m_pszDefaultValue ) + 1; m_pszString = new char[m_StringLength]; memcpy( m_pszString, m_pszDefaultValue, m_StringLength ); - + m_bHasMin = bMin; m_fMinVal = fMin; m_bHasMax = bMax; @@ -1001,7 +1004,7 @@ void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= m_fCompMaxVal = fCompMax; m_bCompetitiveRestrictions = false; - + m_fnChangeCallback = callback; m_fValue = ( float )atof( m_pszString ); @@ -1015,8 +1018,8 @@ void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= } //----------------------------------------------------------------------------- -// Purpose: -// Input : *value - +// Purpose: +// Input : *value - //----------------------------------------------------------------------------- void ConVar::SetValue(const char *value) { @@ -1025,8 +1028,8 @@ void ConVar::SetValue(const char *value) } //----------------------------------------------------------------------------- -// Purpose: -// Input : value - +// Purpose: +// Input : value - //----------------------------------------------------------------------------- void ConVar::SetValue( float value ) { @@ -1035,8 +1038,8 @@ void ConVar::SetValue( float value ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : value - +// Purpose: +// Input : value - //----------------------------------------------------------------------------- void ConVar::SetValue( int value ) { @@ -1055,8 +1058,8 @@ void ConVar::Revert( void ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : minVal - +// Purpose: +// Input : minVal - // Output : true if there is a min set //----------------------------------------------------------------------------- bool ConVar::GetMin( float& minVal ) const @@ -1066,8 +1069,8 @@ bool ConVar::GetMin( float& minVal ) const } //----------------------------------------------------------------------------- -// Purpose: -// Input : maxVal - +// Purpose: +// Input : maxVal - //----------------------------------------------------------------------------- bool ConVar::GetMax( float& maxVal ) const { @@ -1076,8 +1079,8 @@ bool ConVar::GetMax( float& maxVal ) const } //----------------------------------------------------------------------------- -// Purpose: -// Input : minVal - +// Purpose: +// Input : minVal - // Output : true if there is a min set //----------------------------------------------------------------------------- bool ConVar::GetCompMin( float& minVal ) const @@ -1087,8 +1090,8 @@ bool ConVar::GetCompMin( float& minVal ) const } //----------------------------------------------------------------------------- -// Purpose: -// Input : maxVal - +// Purpose: +// Input : maxVal - //----------------------------------------------------------------------------- bool ConVar::GetCompMax( float& maxVal ) const { @@ -1097,9 +1100,9 @@ bool ConVar::GetCompMax( float& maxVal ) const } //----------------------------------------------------------------------------- -// Purpose: Sets that competitive mode is enabled for this var, and then -// attempts to clamp to competitive values. -// Input : maxVal - +// Purpose: Sets that competitive mode is enabled for this var, and then +// attempts to clamp to competitive values. +// Input : maxVal - // Output : true if the value was successfully updated, otherwise false. //----------------------------------------------------------------------------- bool ConVar::SetCompetitiveMode( bool bCompetitive ) @@ -1114,7 +1117,7 @@ bool ConVar::SetCompetitiveMode( bool bCompetitive ) bool bRequiresClamp = ( var->m_bHasCompMin && var->m_fCompMinVal > var->m_fValue ) || ( var->m_bHasCompMax && var->m_fCompMaxVal < var->m_fValue ); - bool bForceToDefault = !var->m_bHasCompMin && !var->m_bHasCompMax + bool bForceToDefault = !var->m_bHasCompMin && !var->m_bHasCompMax && ( fabs( var->m_fValue - ( fDefaultAsFloat = V_atof( var->m_pszDefaultValue ) ) ) > 0.00001f ); if ( bRequiresClamp ) @@ -1127,7 +1130,7 @@ bool ConVar::SetCompetitiveMode( bool bCompetitive ) } // The clamping should've worked, so if it didn't--need to understand why. - Assert( !bRequiresClamp || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) || ( ( !var->m_bHasCompMin || var->m_fCompMinVal <= var->m_fValue ) + Assert( !bRequiresClamp || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) || ( ( !var->m_bHasCompMin || var->m_fCompMinVal <= var->m_fValue ) && ( !var->m_bHasCompMax || var->m_fCompMaxVal >= var->m_fValue ) ) ); Assert( !bForceToDefault || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) || ( var->m_fValue == fDefaultAsFloat ) ); return true; @@ -1136,7 +1139,7 @@ bool ConVar::SetCompetitiveMode( bool bCompetitive ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: // Output : const char //----------------------------------------------------------------------------- const char *ConVar::GetDefault( void ) const @@ -1144,8 +1147,8 @@ const char *ConVar::GetDefault( void ) const return m_pParent->m_pszDefaultValue; } -void ConVar::SetDefault( const char *pszDefault ) -{ +void ConVar::SetDefault( const char *pszDefault ) +{ m_pszDefaultValue = pszDefault ? pszDefault : ""; Assert( m_pszDefaultValue ); } @@ -1213,7 +1216,7 @@ bool ConVarRef::IsValid() const //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void ConVar_PrintFlags( const ConCommandBase *var ) { @@ -1286,7 +1289,7 @@ void ConVar_PrintFlags( const ConCommandBase *var ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void ConVar_PrintDescription( const ConCommandBase *pVar ) { @@ -1313,7 +1316,7 @@ void ConVar_PrintDescription( const ConCommandBase *pVar ) if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) { value = tempVal; - + int intVal = pBounded ? pBounded->GetInt() : var->GetInt(); float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat(); diff --git a/game/shared/interval.cpp b/tier1/interval.cpp similarity index 93% rename from game/shared/interval.cpp rename to tier1/interval.cpp index 38c903599..2fc4de87a 100644 --- a/game/shared/interval.cpp +++ b/tier1/interval.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ //=============================================================================// @@ -14,20 +14,20 @@ #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- -// Purpose: -// Input : *pString - +// Purpose: +// Input : *pString - // Output : interval_t //----------------------------------------------------------------------------- interval_t ReadInterval( const char *pString ) { interval_t tmp; - + tmp.start = 0; tmp.range = 0; char tempString[128]; Q_strncpy( tempString, pString, sizeof(tempString) ); - + char *token = strtok( tempString, "," ); if ( token ) { @@ -43,8 +43,8 @@ interval_t ReadInterval( const char *pString ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : &interval - +// Purpose: +// Input : &interval - // Output : float //----------------------------------------------------------------------------- float RandomInterval( const interval_t &interval ) diff --git a/tier1/mapbase_con_groups.cpp b/tier1/mapbase_con_groups.cpp new file mode 100644 index 000000000..1eeeccafb --- /dev/null +++ b/tier1/mapbase_con_groups.cpp @@ -0,0 +1,193 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: Mapbase classifies certain types of console messages into groups with specific colors. +// +// This is inspired by similar groups seen in CS:GO and Source 2 games. +// +// $NoKeywords: $ +//============================================================================= + +#include +#include +#include +#include "basetypes.h" +#include "tier1.h" +#include "utldict.h" +#include "Color.h" +#include "mapbase_con_groups.h" +#include "KeyValues.h" +#include "filesystem.h" +#include "mapbase_matchers_base.h" + +struct ConGroup_t +{ + ConGroup_t( const char *_pszName, const char *_pszDescription ) + { + pszName = _pszName; + pszDescription = _pszDescription; + _clr.SetColor( 224, 224, 224, 255 ); // Default to a shade of gray + } + + const Color &GetColor() + { + return _clr; + } + + const char *pszName; + const char *pszDescription; + Color _clr; + + bool bDisabled; +}; + +// TODO: Something more reliable? +static bool g_bIncludeConGroupNames = false; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//#define DEFINE_CON_GROUP(id, name, codename) { name, &con_group_##codename##_color } +#define DEFINE_CON_GROUP(id, name, description) { name, description } + +ConGroup_t g_ConGroups[CON_GROUP_MAX] = { + + // General + DEFINE_CON_GROUP( CON_GROUP_MAPBASE_MISC, "Mapbase misc.", "Messages from misc. Mapbase functions, like map-specific files." ), + DEFINE_CON_GROUP( CON_GROUP_PHYSICS, "Physics", "Messages from physics-related events." ), + DEFINE_CON_GROUP( CON_GROUP_IO_SYSTEM, "Entity IO", "Messages from I/O events. (these display in developer 2)" ), + DEFINE_CON_GROUP( CON_GROUP_RESPONSE_SYSTEM, "Response System", "Messages from the Response System, a library primarily used for NPC speech." ), + + // Game + DEFINE_CON_GROUP( CON_GROUP_NPC_AI, "NPC AI", "Messages from NPC AI, etc. which display at various verbose levels." ), + DEFINE_CON_GROUP( CON_GROUP_NPC_SCRIPTS, "NPC scripts", "Messages from scripted_sequence, etc. (these display in developer 2)" ), + DEFINE_CON_GROUP( CON_GROUP_SPEECH_AI, "Speech AI", "Messages from response expressers. (these display in developer 1, 2, etc.)" ), + DEFINE_CON_GROUP( CON_GROUP_CHOREO, "Choreo", "Messages from choreographed scenes. (these display in developer 1, 2, etc.)" ), + + // VScript + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT, "VScript", "Internal messages from VScript not produced by actual scripts." ), + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT_PRINT, "VScript print", "Messages from VScript's 'print' function." ), + +}; + +int FindConGroup( const char *pszName ) +{ + for (int i = 0; i < CON_GROUP_MAX; i++) + { + if (Q_stricmp( pszName, g_ConGroups[i].pszName ) == 0) + return i; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Loads console groups +//----------------------------------------------------------------------------- +void LoadConsoleGroupsFromFile( IBaseFileSystem *filesystem, const char *pszFileName, const char *pathID ) +{ + KeyValues *pGroupRoot = new KeyValues( "ConsoleGroups" ); + + pGroupRoot->LoadFromFile( filesystem, pszFileName, pathID ); + + KeyValues *pGroup = NULL; + for ( pGroup = pGroupRoot->GetFirstTrueSubKey(); pGroup; pGroup = pGroup->GetNextTrueSubKey() ) + { + int index = FindConGroup( pGroup->GetName() ); + if (index != -1) + { + Color msgClr = pGroup->GetColor( "MessageColor" ); + + // Make sure the color isn't 0,0,0,0 before assigning + if (msgClr.GetRawColor() != 0) + g_ConGroups[index]._clr = msgClr; + + g_ConGroups[index].bDisabled = pGroup->GetBool( "Disabled", false ); + } + else + { + Warning( "Invalid console group %s (new groups should be defined in the code)\n", pGroup->GetName() ); + } + } + + pGroupRoot->deleteThis(); +} + +void InitConsoleGroups( IBaseFileSystem *filesystem ) +{ + LoadConsoleGroupsFromFile( filesystem, "scripts/mapbase_con_groups.txt", "MOD" ); + LoadConsoleGroupsFromFile( filesystem, "scripts/mod_con_groups.txt", "MOD" ); +} + +void PrintAllConsoleGroups() +{ + Msg( "============================================================\n" ); + for (int i = 0; i < CON_GROUP_MAX; i++) + { + ConColorMsg( g_ConGroups[i].GetColor(), " # %s", g_ConGroups[i].pszName ); + + if (g_ConGroups[i].bDisabled) + Msg(" [DISABLED]"); + + Msg( " - %s ", g_ConGroups[i].pszDescription ); + + Msg("\n"); + } + Msg( "============================================================\n" ); +} + +void ToggleConsoleGroups( const char *pszQuery ) +{ + bool bMatched = false; + + for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + { + if (Matcher_NamesMatch( pszQuery, g_ConGroups[i].pszName )) + { + Msg( "%s is now %s\n", g_ConGroups[i].pszName, g_ConGroups[i].bDisabled ? "enabled" : "disabled" ); + g_ConGroups[i].bDisabled = !g_ConGroups[i].bDisabled; + bMatched = true; + } + } + + if (!bMatched) + Msg( "No groups matching \"%s\"\n", pszQuery ); +} + +void SetConsoleGroupIncludeNames( bool bToggle ) +{ + g_bIncludeConGroupNames = bToggle; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CGMsg( int level, ConGroupID_t nGroup, const tchar* pMsg, ... ) +{ + // Return early if we're not at this level + if (!IsSpewActive("developer", level)) + return; + + char string[ 2048 ]; + va_list argptr; + va_start( argptr, pMsg ); + Q_vsnprintf( string, sizeof(string), pMsg, argptr ); + va_end( argptr ); + + Assert( nGroup >= 0 ); + Assert( nGroup < CON_GROUP_MAX ); + + ConGroup_t *pGroup = &g_ConGroups[nGroup]; + + if (pGroup->bDisabled) + { + // Do nothing + } + else if (g_bIncludeConGroupNames) + { + ConColorMsg(level, pGroup->GetColor(), "[%s] %s", pGroup->pszName, string); + } + else + { + ConColorMsg(level, pGroup->GetColor(), "%s", string); + } +} diff --git a/tier1/mapbase_matchers_base.cpp b/tier1/mapbase_matchers_base.cpp new file mode 100644 index 000000000..b8979708e --- /dev/null +++ b/tier1/mapbase_matchers_base.cpp @@ -0,0 +1,243 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: General matching functions for things like wildcards and !=. +// +// $NoKeywords: $ +//============================================================================= + +#include "mapbase_matchers_base.h" +#include "convar.h" + +// glibc (Linux) uses these tokens when including , so we must not #define them +#undef max +#undef min +#include +#undef MINMAX_H +#include "minmax.h" + +ConVar mapbase_wildcards_enabled("mapbase_wildcards_enabled", "1", FCVAR_NONE, "Toggles Mapbase's '?' wildcard and true '*' features. Useful for maps that have '?' in their targetnames."); +ConVar mapbase_wildcards_lazy_hack("mapbase_wildcards_lazy_hack", "1", FCVAR_NONE, "Toggles a hack which prevents Mapbase's lazy '?' wildcards from picking up \"???\", the default instance parameter."); +ConVar mapbase_regex_enabled("mapbase_regex_enabled", "1", FCVAR_NONE, "Toggles Mapbase's regex matching handover."); + +//============================================================================= +// These are the "matchers" that compare with wildcards ("any*" for text starting with "any") +// and operators (<3 for numbers less than 3). +// +// Matcher_Regex - Uses regex functions from the std library. +// Matcher_NamesMatch - Based on Valve's original NamesMatch function, using wildcards and regex. +// +// AppearsToBeANumber - Response System-based function which checks if the string might be a number. +//============================================================================= + +// The recursive part of Mapbase's modified version of Valve's NamesMatch(). +bool Matcher_RunCharCompare(const char *pszQuery, const char *szValue) +{ + // This matching model is based off of the ASW SDK + while ( *szValue && *pszQuery ) + { + char cName = *szValue; + char cQuery = *pszQuery; + if ( cName != cQuery && tolower(cName) != tolower(cQuery) ) // people almost always use lowercase, so assume that first + { + // Now we'll try the new and improved Mapbase wildcards! + switch (*pszQuery) + { + case '*': + { + // Return true at classic trailing * + if ( *(pszQuery+1) == 0 ) + return true; + + if (mapbase_wildcards_enabled.GetBool()) + { + // There's text after this * which we need to test. + // This recursion allows for multiple wildcards + int vlen = Q_strlen(szValue); + ++pszQuery; + for (int i = 0; i < vlen; i++) + { + if (Matcher_RunCharCompare(pszQuery, szValue + i)) + return true; + } + } + return false; + } break; + case '?': + // Just skip if we're capable of lazy wildcards + if (mapbase_wildcards_enabled.GetBool()) + break; + default: + return false; + } + } + ++szValue; + ++pszQuery; + } + + // Include a classic trailing * check for when szValue is something like "value" and pszQuery is "value*" + return ( ( *pszQuery == 0 && *szValue == 0 ) || *pszQuery == '*' ); +} + +// Regular expressions based off of the std library. +// The C++ is strong in this one. +bool Matcher_Regex(const char *pszQuery, const char *szValue) +{ + std::regex regex; + + // Since I can't find any other way to check for valid regex, + // use a try-catch here to see if it throws an exception. + try { regex = std::regex(pszQuery); } + catch (std::regex_error &e) + { + Msg("Invalid regex \"%s\" (%s)\n", pszQuery, e.what()); + return false; + } + + std::match_results results; + bool bMatch = std::regex_match( szValue, results, regex ); + if (!bMatch) + return false; + + // Only match the *whole* string + return Q_strlen(results.str(0).c_str()) == Q_strlen(szValue); +} + +// The entry point for Mapbase's modified version of Valve's NamesMatch(). +bool Matcher_NamesMatch(const char *pszQuery, const char *szValue) +{ + if ( szValue == NULL ) + return (*pszQuery == 0 || *pszQuery == '*'); + + // If the pointers are identical, we're identical + if ( szValue == pszQuery ) + return true; + + // Check for regex + if ( *pszQuery == '@' && mapbase_regex_enabled.GetBool() ) + { + // Make sure it has a forward slash + // (prevents confusion with instance fixup escape) + if (*(pszQuery+1) == '/') + { + return Matcher_Regex( pszQuery+2, szValue ); + } + } + else if (pszQuery[0] == '?' && pszQuery[1] == '?' && pszQuery[2] == '?' && mapbase_wildcards_lazy_hack.GetBool()) + { + // HACKHACK: There's a nasty issue where instances with blank parameters use "???", but Mapbase's lazy wildcard code + // recognizes this as essentially meaning "any name with 3 characters". This is a serious problem when the instance + // specifically expects the game to interpret "???" as a blank space, such as with damage filters, which crash when targeting + // a non-filter entity. + return false; + } + + return Matcher_RunCharCompare( pszQuery, szValue ); +} + +bool Matcher_NamesMatch_Classic(const char *pszQuery, const char *szValue) +{ + if ( szValue == NULL ) + return (!pszQuery || *pszQuery == 0 || *pszQuery == '*'); + + // If the pointers are identical, we're identical + if ( szValue == pszQuery ) + return true; + + while ( *szValue && *pszQuery ) + { + unsigned char cName = *szValue; + unsigned char cQuery = *pszQuery; + // simple ascii case conversion + if ( cName == cQuery ) + ; + else if ( cName - 'A' <= (unsigned char)'Z' - 'A' && cName - 'A' + 'a' == cQuery ) + ; + else if ( cName - 'a' <= (unsigned char)'z' - 'a' && cName - 'a' + 'A' == cQuery ) + ; + else + break; + ++szValue; + ++pszQuery; + } + + if ( *pszQuery == 0 && *szValue == 0 ) + return true; + + // @TODO (toml 03-18-03): Perhaps support real wildcards. Right now, only thing supported is trailing * + if ( *pszQuery == '*' ) + return true; + + return false; +} + +bool Matcher_NamesMatch_MutualWildcard(const char *pszQuery, const char *szValue) +{ + if ( szValue == NULL ) + return (!pszQuery || *pszQuery == 0 || *pszQuery == '*'); + + if ( pszQuery == NULL ) + return (!szValue || *szValue == 0 || *szValue == '*'); + + // If the pointers are identical, we're identical + if ( szValue == pszQuery ) + return true; + + while ( *szValue && *pszQuery ) + { + unsigned char cName = *szValue; + unsigned char cQuery = *pszQuery; + // simple ascii case conversion + if ( cName == cQuery ) + ; + else if ( cName - 'A' <= (unsigned char)'Z' - 'A' && cName - 'A' + 'a' == cQuery ) + ; + else if ( cName - 'a' <= (unsigned char)'z' - 'a' && cName - 'a' + 'A' == cQuery ) + ; + else + break; + ++szValue; + ++pszQuery; + } + + if ( *pszQuery == 0 && *szValue == 0 ) + return true; + + // @TODO (toml 03-18-03): Perhaps support real wildcards. Right now, only thing supported is trailing * + if ( *pszQuery == '*' || *szValue == '*' ) + return true; + + return false; +} + +// Returns true if a string contains a wildcard. +bool Matcher_ContainsWildcard(const char *pszQuery) +{ + if ( pszQuery == NULL ) + return false; + + while ( *pszQuery ) + { + unsigned char cQuery = *pszQuery; + if (cQuery == '*' || cQuery == '?') + return true; + ++pszQuery; + } + + return false; +} + +// Matcher_Compare is a deprecated alias originally used when Matcher_Match didn't support wildcards. +/* +bool Matcher_Compare(const char *pszQuery, const char *szValue) +{ + return Matcher_Match(pszQuery, szValue); +#if 0 + // I have to do this so wildcards could test *before* the response system comparison. + // I know it removes the operators twice, but I won't worry about it. + bool match = Matcher_NamesMatch(Matcher_RemoveOperators(pszQuery), szValue); + if (match) + return Matcher_Match(pszQuery, szValue); + return false; +#endif +} +*/ diff --git a/tier1/strtools.cpp b/tier1/strtools.cpp index a967fbfe0..b540f6338 100644 --- a/tier1/strtools.cpp +++ b/tier1/strtools.cpp @@ -378,7 +378,7 @@ int64 V_atoi64( const char *str ) int64 val; int64 sign; int64 c; - + Assert( str ); if (*str == '-') { @@ -394,7 +394,7 @@ int64 V_atoi64( const char *str ) { sign = 1; } - + val = 0; // @@ -416,7 +416,7 @@ int64 V_atoi64( const char *str ) return val*sign; } } - + // // check for character // @@ -424,7 +424,7 @@ int64 V_atoi64( const char *str ) { return sign * str[1]; } - + // // assume decimal // @@ -435,7 +435,7 @@ int64 V_atoi64( const char *str ) return val*sign; val = val*10 + c - '0'; } - + return 0; } @@ -493,7 +493,7 @@ uint64 V_atoui64( const char *str ) } int V_atoi( const char *str ) -{ +{ return (int)V_atoi64( str ); } @@ -597,7 +597,7 @@ float V_atof (const char *str) } //----------------------------------------------------------------------------- -// Normalizes a float string in place. +// Normalizes a float string in place. // // (removes leading zeros, trailing zeros after the decimal point, and the decimal point itself where possible) //----------------------------------------------------------------------------- @@ -634,7 +634,7 @@ char const* V_stristr( char const* pStr, char const* pSearch ) AssertValidStringPtr(pStr); AssertValidStringPtr(pSearch); - if (!pStr || !pSearch) + if (!pStr || !pSearch) return 0; char const* pLetter = pStr; @@ -689,7 +689,7 @@ char const* V_strnistr( char const* pStr, char const* pSearch, int n ) AssertValidStringPtr(pStr); AssertValidStringPtr(pSearch); - if (!pStr || !pSearch) + if (!pStr || !pSearch) return 0; char const* pLetter = pStr; @@ -816,7 +816,7 @@ int V_snwprintf( wchar_t *pDest, int maxLen, const wchar_t *pFormat, ... ) len = maxLen; pDest[maxLen-1] = 0; } - + return len; } @@ -934,7 +934,7 @@ char *V_strncat(char *pDest, const char *pSrc, size_t destBufferSize, int max_ch Assert( (ptrdiff_t)destBufferSize >= 0 ); AssertValidStringPtr( pDest); AssertValidStringPtr( pSrc ); - + size_t len = strlen(pDest); size_t srclen = strlen( pSrc ); if ( max_chars_to_copy <= COPY_ALL_CHARACTERS ) @@ -966,7 +966,7 @@ wchar_t *V_wcsncat( INOUT_Z_CAP(cchDest) wchar_t *pDest, const wchar_t *pSrc, si size_t charstocopy = (size_t)0; Assert( (ptrdiff_t)cchDest >= 0 ); - + size_t len = wcslen(pDest); size_t srclen = wcslen( pSrc ); if ( max_chars_to_copy <= COPY_ALL_CHARACTERS ) @@ -997,9 +997,9 @@ wchar_t *V_wcsncat( INOUT_Z_CAP(cchDest) wchar_t *pDest, const wchar_t *pSrc, si //----------------------------------------------------------------------------- // Purpose: Converts value into x.xx MB/ x.xx KB, x.xx bytes format, including commas -// Input : value - -// 2 - -// false - +// Input : value - +// 2 - +// false - // Output : char //----------------------------------------------------------------------------- #define NUM_PRETIFYMEM_BUFFERS 8 @@ -1179,7 +1179,7 @@ char *V_pretifynum( int64 inputValue ) // behavior when used in names, web pages, chat windows, etc. // // characters in this set are removed from the beginning and/or end of strings -// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() +// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() //----------------------------------------------------------------------------- bool Q_IsMeanSpaceW( wchar_t wch ) { @@ -1287,7 +1287,7 @@ bool Q_RemoveAllEvilCharacters( char *pch ) // Null terminate pwch[nWalk-1] = L'\0'; - + // copy back, if necessary if ( bStrippedWhitespace ) @@ -1414,7 +1414,7 @@ int _V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInByt Assert( cubDestSizeInBytes >= sizeof( *pUnicode ) ); AssertValidWritePtr(pUnicode); AssertValidReadPtr(pUCS2); - + pUnicode[0] = 0; #ifdef _WIN32 int cchResult = V_wcslen( pUCS2 ); @@ -1426,7 +1426,7 @@ int _V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInByt size_t nMaxUTF8 = cubDestSizeInBytes; char *pIn = (char *)pUCS2; char *pOut = (char *)pUnicode; - if ( conv_t > (void*)0 ) + if ( conv_t ) { cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); iconv_close( conv_t ); @@ -1437,7 +1437,7 @@ int _V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInByt } #endif pUnicode[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0; - return cchResult; + return cchResult; } @@ -1466,7 +1466,7 @@ int _V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, i size_t nMaxUCS2 = cubDestSizeInBytes; char *pIn = (char*)pUnicode; char *pOut = pUCS2; - if ( conv_t > (void*)0 ) + if ( conv_t ) { cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUCS2 ); iconv_close( conv_t ); @@ -1478,7 +1478,7 @@ int _V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, i #else #error Must be implemented for this platform #endif - return cchResult; + return cchResult; } @@ -1489,7 +1489,7 @@ int _V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes ) { AssertValidStringPtr(pUTF8, cubDestSizeInBytes); AssertValidReadPtr(pUCS2); - + pUTF8[0] = 0; #ifdef _WIN32 // under win32 wchar_t == ucs2, sigh @@ -1514,7 +1514,7 @@ int _V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes ) size_t nMaxUTF8 = cubDestSizeInBytes - 1; char *pIn = (char *)pUCS2; char *pOut = (char *)pUTF8; - if ( conv_t > (void*)0 ) + if ( conv_t ) { const size_t nBytesToWrite = nMaxUTF8; cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); @@ -1532,7 +1532,7 @@ int _V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes ) } #endif pUTF8[cubDestSizeInBytes - 1] = 0; - return cchResult; + return cchResult; } @@ -1559,7 +1559,7 @@ int _V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDes size_t nMaxUTF8 = cubDestSizeInBytes; char *pIn = (char *)pUTF8; char *pOut = (char *)pUCS2; - if ( conv_t > (void*)0 ) + if ( conv_t ) { cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); iconv_close( conv_t ); @@ -1571,14 +1571,14 @@ int _V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDes } #endif pUCS2[ (cubDestSizeInBytes/sizeof(ucs2)) - 1] = 0; - return cchResult; + return cchResult; } //----------------------------------------------------------------------------- // Purpose: Returns the 4 bit nibble for a hex character -// Input : c - +// Input : c - // Output : unsigned char //----------------------------------------------------------------------------- unsigned char V_nibble( char c ) @@ -1605,11 +1605,11 @@ unsigned char V_nibble( char c ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *in - -// numchars - -// *out - -// maxoutputbytes - +// Purpose: +// Input : *in - +// numchars - +// *out - +// maxoutputbytes - //----------------------------------------------------------------------------- void V_hextobinary( char const *in, int numchars, byte *out, int maxoutputbytes ) { @@ -1627,20 +1627,20 @@ void V_hextobinary( char const *in, int numchars, byte *out, int maxoutputbytes int i; p = out; - for ( i = 0; - ( i < numchars ) && ( ( p - out ) < maxoutputbytes ); + for ( i = 0; + ( i < numchars ) && ( ( p - out ) < maxoutputbytes ); i+=2, p++ ) { - *p = ( V_nibble( in[i] ) << 4 ) | V_nibble( in[i+1] ); + *p = ( V_nibble( in[i] ) << 4 ) | V_nibble( in[i+1] ); } } //----------------------------------------------------------------------------- -// Purpose: -// Input : *in - -// inputbytes - -// *out - -// outsize - +// Purpose: +// Input : *in - +// inputbytes - +// *out - +// outsize - //----------------------------------------------------------------------------- void V_binarytohex( const byte *in, int inputbytes, char *out, int outsize ) { @@ -1670,9 +1670,9 @@ bool PATHSEPARATOR( char c ) //----------------------------------------------------------------------------- // Purpose: Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator) -// Input : *in - -// *out - -// maxlen - +// Input : *in - +// *out - +// maxlen - //----------------------------------------------------------------------------- void V_FileBase( const char *in, char *out, int maxlen ) { @@ -1689,19 +1689,19 @@ void V_FileBase( const char *in, char *out, int maxlen ) int len, start, end; len = V_strlen( in ); - + // scan backward for '.' end = len - 1; while ( end&& in[end] != '.' && !PATHSEPARATOR( in[end] ) ) { end--; } - + if ( in[end] != '.' ) // no '.', copy to end { end = len-1; } - else + else { end--; // Found ',', copy to left of '.' } @@ -1717,7 +1717,7 @@ void V_FileBase( const char *in, char *out, int maxlen ) { start = 0; } - else + else { start++; } @@ -1732,8 +1732,8 @@ void V_FileBase( const char *in, char *out, int maxlen ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *ppath - +// Purpose: +// Input : *ppath - //----------------------------------------------------------------------------- void V_StripTrailingSlash( char *ppath ) { @@ -1750,8 +1750,8 @@ void V_StripTrailingSlash( char *ppath ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *ppline - +// Purpose: +// Input : *ppline - //----------------------------------------------------------------------------- void V_StripTrailingWhitespace( char *ppline ) { @@ -1768,8 +1768,8 @@ void V_StripTrailingWhitespace( char *ppline ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *ppline - +// Purpose: +// Input : *ppline - //----------------------------------------------------------------------------- void V_StripLeadingWhitespace( char *ppline ) { @@ -1788,8 +1788,8 @@ void V_StripLeadingWhitespace( char *ppline ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *ppline - +// Purpose: +// Input : *ppline - //----------------------------------------------------------------------------- void V_StripSurroundingQuotes( char *ppline ) { @@ -1805,14 +1805,14 @@ void V_StripSurroundingQuotes( char *ppline ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *in - -// *out - -// outSize - +// Purpose: +// Input : *in - +// *out - +// outSize - //----------------------------------------------------------------------------- void V_StripExtension( const char *in, char *out, int outSize ) { - // Find the last dot. If it's followed by a dot or a slash, then it's part of a + // Find the last dot. If it's followed by a dot or a slash, then it's part of a // directory specifier like ../../somedir/./blah. // scan backward for '.' @@ -1842,10 +1842,10 @@ void V_StripExtension( const char *in, char *out, int outSize ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *path - -// *extension - -// pathStringLength - +// Purpose: +// Input : *path - +// *extension - +// pathStringLength - //----------------------------------------------------------------------------- void V_DefaultExtension( char *path, const char *extension, int pathStringLength ) { @@ -1865,7 +1865,7 @@ void V_DefaultExtension( char *path, const char *extension, int pathStringLength if (*src == '.') { // it has an extension - return; + return; } src--; } @@ -1876,9 +1876,9 @@ void V_DefaultExtension( char *path, const char *extension, int pathStringLength //----------------------------------------------------------------------------- // Purpose: Force extension... -// Input : *path - -// *extension - -// pathStringLength - +// Input : *path - +// *extension - +// pathStringLength - //----------------------------------------------------------------------------- void V_SetExtension( char *path, const char *extension, int pathStringLength ) { @@ -1899,7 +1899,7 @@ void V_SetExtension( char *path, const char *extension, int pathStringLength ) //----------------------------------------------------------------------------- // Purpose: Remove final filename from string -// Input : *path - +// Input : *path - // Output : void V_StripFilename //----------------------------------------------------------------------------- void V_StripFilename (char *path) @@ -1910,7 +1910,7 @@ void V_StripFilename (char *path) if ( length <= 0 ) return; - while ( length > 0 && + while ( length > 0 && !PATHSEPARATOR( path[length] ) ) { length--; @@ -1929,8 +1929,8 @@ void V_StripFilename (char *path) //----------------------------------------------------------------------------- // Purpose: Changes all '/' or '\' characters into separator -// Input : *pname - -// separator - +// Input : *pname - +// separator - //----------------------------------------------------------------------------- void V_FixSlashes( char *pname, char separator /* = CORRECT_PATH_SEPARATOR */ ) { @@ -1956,8 +1956,8 @@ void V_FixDoubleSlashes( char *pStr ) { if ( (pStr[i] == '/' || pStr[i] == '\\') && (pStr[i+1] == '/' || pStr[i+1] == '\\') ) { - // This means there's a double slash somewhere past the start of the filename. That - // can happen in Hammer if they use a material in the root directory. You'll get a filename + // This means there's a double slash somewhere past the start of the filename. That + // can happen in Hammer if they use a material in the root directory. You'll get a filename // that looks like 'materials\\blah.vmt' V_memmove( &pStr[i], &pStr[i+1], len - i ); --len; @@ -1967,17 +1967,17 @@ void V_FixDoubleSlashes( char *pStr ) //----------------------------------------------------------------------------- // Purpose: Strip off the last directory from dirName -// Input : *dirName - -// maxlen - +// Input : *dirName - +// maxlen - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool V_StripLastDir( char *dirName, int maxlen ) { - if( dirName[0] == 0 || - !V_stricmp( dirName, "./" ) || + if( dirName[0] == 0 || + !V_stricmp( dirName, "./" ) || !V_stricmp( dirName, ".\\" ) ) return false; - + int len = V_strlen( dirName ); Assert( len < maxlen ); @@ -2012,7 +2012,7 @@ bool V_StripLastDir( char *dirName, int maxlen ) //----------------------------------------------------------------------------- -// Purpose: Returns a pointer to the beginning of the unqualified file name +// Purpose: Returns a pointer to the beginning of the unqualified file name // (no path information) // Input: in - file name (may be unqualified, relative or absolute path) // Output: pointer to unqualified file name @@ -2050,10 +2050,10 @@ void V_ComposeFileName( const char *path, const char *filename, char *dest, int //----------------------------------------------------------------------------- -// Purpose: -// Input : *path - -// *dest - -// destSize - +// Purpose: +// Input : *path - +// *dest - +// destSize - // Output : void V_ExtractFilePath //----------------------------------------------------------------------------- bool V_ExtractFilePath (const char *path, char *dest, int destSize ) @@ -2082,10 +2082,10 @@ bool V_ExtractFilePath (const char *path, char *dest, int destSize ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *path - -// *dest - -// destSize - +// Purpose: +// Input : *path - +// *dest - +// destSize - // Output : void V_ExtractFileExtension //----------------------------------------------------------------------------- void V_ExtractFileExtension( const char *path, char *dest, int destSize ) @@ -2099,7 +2099,7 @@ void V_ExtractFileExtension( const char *path, char *dest, int destSize ) //----------------------------------------------------------------------------- // Purpose: Returns a pointer to the file extension within a file name string -// Input: in - file name +// Input: in - file name // Output: pointer to beginning of extension (after the "."), or NULL // if there is no extension //----------------------------------------------------------------------------- @@ -2117,7 +2117,7 @@ const char * V_GetFileExtension( const char * path ) // check to see if the '.' is part of a pathname if (src == path || PATHSEPARATOR( *src ) ) - { + { return NULL; // no extension } @@ -2127,8 +2127,8 @@ const char * V_GetFileExtension( const char * path ) //----------------------------------------------------------------------------- // Purpose: Returns a pointer to the filename part of a path string -// Input: in - file name -// Output: pointer to beginning of filename (after the "/"). If there were no /, +// Input: in - file name +// Output: pointer to beginning of filename (after the "/"). If there were no /, // output is identical to input //----------------------------------------------------------------------------- const char * V_GetFileName( const char * path ) @@ -2225,7 +2225,7 @@ void V_AppendSlash( char *pStr, int strSize ) { if ( len+1 >= strSize ) Error( "V_AppendSlash: ran out of space on %s.", pStr ); - + pStr[len] = CORRECT_PATH_SEPARATOR; pStr[len+1] = 0; } @@ -2283,7 +2283,7 @@ bool V_MakeRelativePath( const char *pFullPath, const char *pDirectory, char *pR // Strip out common parts of the path const char *pLastCommonPath = NULL; const char *pLastCommonDir = NULL; - while ( *pPath && ( FastToLower( *pPath ) == FastToLower( *pDir ) || + while ( *pPath && ( FastToLower( *pPath ) == FastToLower( *pDir ) || ( PATHSEPARATOR( *pPath ) && ( PATHSEPARATOR( *pDir ) || (*pDir == 0) ) ) ) ) { if ( PATHSEPARATOR( *pPath ) ) @@ -2385,7 +2385,7 @@ static bool CopyToMaxChars( char *pOut, int outSize, const char *pIn, int nChars ++pIn; --nCharsToCopy; } - + pOut[iOut] = 0; return true; } @@ -2406,8 +2406,8 @@ void V_FixupPathName( char *pOut, size_t nOutLen, const char *pPath ) // Returns true if it completed successfully. // If it would overflow pOut, it fills as much as it can and returns false. -bool V_StrSubst( - const char *pIn, +bool V_StrSubst( + const char *pIn, const char *pMatch, const char *pReplaceWith, char *pOut, @@ -2433,7 +2433,7 @@ bool V_StrSubst( int copyLen = pTestPos - pInStart; if ( !CopyToMaxChars( pOutPos, nRemainingOut, pInStart, copyLen ) ) return false; - + // Did we hit the end of the output string? if ( copyLen > nRemainingOut-1 ) return false; @@ -2446,7 +2446,7 @@ bool V_StrSubst( return false; pInStart += copyLen + replaceFromLen; - pOutPos += replaceToLen; + pOutPos += replaceToLen; } else { @@ -2541,7 +2541,7 @@ void V_StrSlice( const char *pStr, int firstChar, int lastCharNonInclusive, char { if ( outSize == 0 ) return; - + int length = strlen( pStr ); // Fixup the string indices. @@ -2714,7 +2714,7 @@ char *V_AddBackSlashesToSpecialChars( char const *pSrc ) } char *pRet = new char[ nSpaceNeeded + 1 ]; // +1 for null char *pOut = pRet; - + for( char const *pScan = pSrc; *pScan; pScan++ ) { bool bIsSpecial = false; @@ -2783,7 +2783,7 @@ void Q_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, in // These are the characters allowed by both the original RFC 1738 and the latest RFC 3986. // Current specs also allow '~', but that is forbidden under original RFC 1738. if ( !( pchSource[i] >= 'a' && pchSource[i] <= 'z' ) && !( pchSource[i] >= 'A' && pchSource[i] <= 'Z' ) && !(pchSource[i] >= '0' && pchSource[i] <= '9' ) - && pchSource[i] != '-' && pchSource[i] != '_' && pchSource[i] != '.' + && pchSource[i] != '-' && pchSource[i] != '_' && pchSource[i] != '.' ) { if ( bUsePlusForSpace && pchSource[i] == ' ' ) @@ -2897,7 +2897,7 @@ size_t Q_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char } //----------------------------------------------------------------------------- -// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. +// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. // This version of the call isn't a strict RFC implementation, but uses + for space as is // the standard in HTML form encoding, despite it not being part of the RFC. // @@ -2910,7 +2910,7 @@ void Q_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourc //----------------------------------------------------------------------------- -// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. +// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. // This version of the call isn't a strict RFC implementation, but uses + for space as is // the standard in HTML form encoding, despite it not being part of the RFC. // @@ -2924,7 +2924,7 @@ size_t Q_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEnco //----------------------------------------------------------------------------- -// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. +// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. // This version will not encode space as + (which HTML form encoding uses despite not being part of the RFC) // // Dest buffer should be at least as large as source buffer to guarantee room for decode. @@ -2936,7 +2936,7 @@ void Q_URLEncodeRaw( char *pchDest, int nDestLen, const char *pchSource, int nSo //----------------------------------------------------------------------------- -// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. +// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2. // This version will not recognize + as a space (which HTML form encoding uses despite not being part of the RFC) // // Dest buffer should be at least as large as source buffer to guarantee room for decode. @@ -2951,7 +2951,7 @@ size_t Q_URLDecodeRaw( char *pchDecodeDest, int nDecodeDestLen, const char *pchE extern "C" void qsort_s( void *base, size_t num, size_t width, int (*compare )(void *, const void *, const void *), void * context ); #endif -void V_qsort_s( void *base, size_t num, size_t width, int ( __cdecl *compare )(void *, const void *, const void *), void * context ) +void V_qsort_s( void *base, size_t num, size_t width, int ( __cdecl *compare )(void *, const void *, const void *), void * context ) { #if defined(OSX) || defined(PLATFORM_BSD) // the arguments are swapped 'round on the mac - awesome, huh? @@ -3061,7 +3061,7 @@ void V_LogMultiline( bool input, char const *label, const char *data, size_t len const char * direction = (input ? " << " : " >> "); const size_t LINE_SIZE = 24; char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1]; - while (len > 0) + while (len > 0) { V_memset(asc_line, ' ', sizeof(asc_line)); V_memset(hex_line, ' ', sizeof(hex_line)); @@ -3633,7 +3633,7 @@ bool V_BBCodeToHTML( OUT_Z_CAP( nDestSize ) char *pDest, const int nDestSize, ch // behavior when used in names, web pages, chat windows, etc. // // characters in this set are removed from the beginning and/or end of strings -// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() +// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() //----------------------------------------------------------------------------- bool V_IsMeanUnderscoreW( wchar_t wch ) { @@ -3660,7 +3660,7 @@ bool V_IsMeanUnderscoreW( wchar_t wch ) // behavior when used in names, web pages, chat windows, etc. // // characters in this set are removed from the beginning and/or end of strings -// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() +// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() //----------------------------------------------------------------------------- bool V_IsMeanSpaceW( wchar_t wch ) { @@ -3744,7 +3744,7 @@ bool V_IsMeanSpaceW( wchar_t wch ) // Many games don't cope with these characters well, and end up providing opportunities // for griefing others. For example, a user might join a game with a malformed player // name and it turns out that player name can't be selected or typed into the admin -// console or UI to mute, kick, or ban the disruptive player. +// console or UI to mute, kick, or ban the disruptive player. // // Ideally, we'd perfectly support these end-to-end but we never realistically will. // The benefit of doing so far outweighs the cost, anyway. @@ -3783,7 +3783,7 @@ bool V_IsValidDomainNameCharacter( const char *pch, int *pAdvanceBytes ) *pAdvanceBytes = 0; - // We allow unicode in Domain Names without the an encoding unless it corresponds to + // We allow unicode in Domain Names without the an encoding unless it corresponds to // a whitespace or control sequence or something we think is an underscore looking thing. // If this character is the start of a UTF-8 sequence, try decoding it. unsigned char ch = (unsigned char)*pch; @@ -4167,7 +4167,7 @@ void V_StripAndPreserveHTMLCore( CUtlBuffer *pbuffer, const char *pchHTML, const const char *szTag = rgszPreserveTags[ iTag ]; int cchTag = Q_strlen( szTag ); - //make sure characters match, and are followed by some non-alnum char + //make sure characters match, and are followed by some non-alnum char // so "i" can match or , but not if ( Q_strnicmp( szTag, szTagStart, cchTag ) == 0 && !V_isalnum( szTagStart[ cchTag ] ) ) { @@ -4299,7 +4299,7 @@ void V_StripAndPreserveHTMLCore( CUtlBuffer *pbuffer, const char *pchHTML, const pbuffer->PutChar( ' ' ); bLastCharWasWhitespace = true; } - // don't put anything for whitespace if the previous character was whitespace + // don't put anything for whitespace if the previous character was whitespace // (effectively trimming all blocks of whitespace down to a single ' ') } else diff --git a/tier1/tier1.vpc b/tier1/tier1.vpc index f4016ee1c..7227383d2 100644 --- a/tier1/tier1.vpc +++ b/tier1/tier1.vpc @@ -5,7 +5,7 @@ //----------------------------------------------------------------------------- $macro SRCDIR ".." - + $include "$SRCDIR\vpc_scripts\source_lib_base.vpc" $Configuration @@ -39,6 +39,7 @@ $Project "tier1" $File "generichash.cpp" $File "ilocalize.cpp" $File "interface.cpp" + $File "interval.cpp" $File "KeyValues.cpp" $File "keyvaluesjson.cpp" $File "kvpacker.cpp" @@ -58,7 +59,7 @@ $Project "tier1" } } } - + $File "processor_detect_linux.cpp" [$POSIX] $File "qsort_s.cpp" [$LINUXALL||$PS3] $File "rangecheckedvar.cpp" @@ -79,6 +80,8 @@ $Project "tier1" $File "snappy.cpp" $File "snappy-sinksource.cpp" $File "snappy-stubs-internal.cpp" + $File "mapbase_con_groups.cpp" [$MAPBASE] + $File "mapbase_matchers_base.cpp" [$MAPBASE] } // Select bits from the LZMA SDK to support lzmaDecoder.h @@ -121,6 +124,8 @@ $Project "tier1" $File "$SRCDIR\public\tier1\kvpacker.h" $File "$SRCDIR\public\tier1\lzmaDecoder.h" $File "$SRCDIR\public\tier1\lzss.h" + $File "$SRCDIR\public\tier1\mapbase_con_groups.h" [$MAPBASE] + $File "$SRCDIR\public\tier1\mapbase_matchers_base.h" [$MAPBASE] $File "$SRCDIR\public\tier1\mempool.h" $File "$SRCDIR\public\tier1\memstack.h" $File "$SRCDIR\public\tier1\netadr.h" diff --git a/tier1/wscript b/tier1/wscript index 0a5f684b5..54db8daa5 100755 --- a/tier1/wscript +++ b/tier1/wscript @@ -30,6 +30,7 @@ def build(bld): 'generichash.cpp', 'ilocalize.cpp', 'interface.cpp', + 'interval.cpp', 'KeyValues.cpp', 'keyvaluesjson.cpp', 'kvpacker.cpp', @@ -43,6 +44,8 @@ def build(bld): 'reliabletimer.cpp', 'snappy-sinksource.cpp', 'snappy-stubs-internal.cpp', + 'mapbase_con_groups.h', # [$MAPBASE] + 'mapbase_matchers_base.h', # [$MAPBASE] 'snappy.cpp', 'sparsematrix.cpp', 'splitstring.cpp', @@ -86,7 +89,7 @@ def build(bld): libs += ['RPCRT4'] elif bld.env.DEST_OS == "darwin": libs += ['ICONV'] - + bld.stlib( source = source, target = PROJECT_NAME, diff --git a/utils/common/threads.cpp b/utils/common/threads.cpp index 74e457a9c..e0af88f0e 100644 --- a/utils/common/threads.cpp +++ b/utils/common/threads.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $Workfile: $ // $Date: $ @@ -19,7 +19,13 @@ #include "threads.h" #include "pacifier.h" +#ifdef MAPBASE +// This was suggested in that Source 2013 pull request that fixed Vrad. +// I trust their judgement on this. +#define MAX_THREADS 32 +#else #define MAX_THREADS 16 +#endif class CRunThreadsData @@ -83,7 +89,7 @@ void ThreadWorkerFunction( int iThread, void *pUserData ) work = GetThreadWork (); if (work == -1) break; - + workfunction( iThread, work ); } } @@ -92,7 +98,7 @@ void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, ThreadWorkerFn { if (numthreads == -1) ThreadSetDefault (); - + workfunction = func; RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction); } @@ -218,7 +224,7 @@ void RunThreads_End() threaded = false; } - + /* ============= @@ -241,7 +247,7 @@ void RunThreadsOn( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pU return; #endif - + RunThreads_Start( fn, pUserData ); RunThreads_End(); diff --git a/utils/common/threads.h b/utils/common/threads.h index 0908b67ad..3d50e1fdb 100644 --- a/utils/common/threads.h +++ b/utils/common/threads.h @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $Workfile: $ // $Date: $ @@ -18,7 +18,14 @@ // Arrays that are indexed by thread should always be MAX_TOOL_THREADS+1 // large so THREADINDEX_MAIN can be used from the main thread. + +#ifdef MAPBASE +// This was suggested in that Source 2013 pull request that fixed Vrad. +// I trust their judgement on this. +#define MAX_TOOL_THREADS 32 +#else #define MAX_TOOL_THREADS 16 +#endif // MAPBASE #define THREADINDEX_MAIN (MAX_TOOL_THREADS) diff --git a/utils/common/utilmatlib.cpp b/utils/common/utilmatlib.cpp index f2dc49fa8..0b13e6981 100644 --- a/utils/common/utilmatlib.cpp +++ b/utils/common/utilmatlib.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $Workfile: $ // $Date: $ @@ -24,7 +24,7 @@ void LoadMaterialSystemInterface( CreateInterfaceFn fileSystemFactory ) { if( g_pMaterialSystem ) return; - + // materialsystem.dll should be in the path, it's in bin along with vbsp. const char *pDllName = "materialsystem.dll"; CSysModule *materialSystemDLLHInst; @@ -59,6 +59,7 @@ void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn file LoadMaterialSystemInterface( fileSystemFactory ); MaterialSystem_Config_t config; g_pMaterialSystem->OverrideConfig( config, false ); + g_pMaterialSystem->ModInit(); } void ShutdownMaterialSystem( ) @@ -74,7 +75,7 @@ MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, b { IMaterial *pMat = g_pMaterialSystem->FindMaterial( materialName, TEXTURE_GROUP_OTHER, bComplain ); MaterialSystemMaterial_t matHandle = pMat; - + if ( pFound ) { *pFound = true; @@ -92,12 +93,12 @@ void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, IMaterial *material = ( IMaterial * )materialHandle; bool translucent; retVal = material->GetPreviewImageProperties( width, height, &dummyImageFormat, &translucent ); - if (retVal != MATERIAL_PREVIEW_IMAGE_OK ) + if (retVal != MATERIAL_PREVIEW_IMAGE_OK ) { #if 0 - if (retVal == MATERIAL_PREVIEW_IMAGE_BAD ) + if (retVal == MATERIAL_PREVIEW_IMAGE_BAD ) { - Error( "problem getting preview image for %s", + Error( "problem getting preview image for %s", g_pMaterialSystem->GetMaterialName( materialInfo[matID].materialHandle ) ); } #else diff --git a/utils/vbsp/csg.cpp b/utils/vbsp/csg.cpp index 5de1d680a..8c8019bb5 100644 --- a/utils/vbsp/csg.cpp +++ b/utils/vbsp/csg.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -29,7 +29,7 @@ evaluate split side cost = 0 for all sides for all sides - get + get if side splits side and splitside is on same child cost++; } @@ -199,7 +199,7 @@ bspbrush_t *ClipBrushToBox (bspbrush_t *brush, const Vector& clipmins, const Vec for (i=0 ; inumsides ; i++) { p = brush->sides[i].planenum & ~1; - if (p == maxplanenums[0] || p == maxplanenums[1] + if (p == maxplanenums[0] || p == maxplanenums[1] || p == minplanenums[0] || p == minplanenums[1]) { brush->sides[i].texinfo = TEXINFO_NODE; @@ -297,7 +297,7 @@ void CopyMatchingTexinfos( side_t *pDestSides, int numDestSides, const bspbrush_ mapbrush_t *pSourceBrush = pSource->original; Assert( pSourceBrush ); - const side_t *pSourceSide = pSourceBrush->original_sides; + const side_t *pSourceSide = pSourceBrush->original_sides; const side_t *pBestSide = NULL; float flBestDot = -1.0f; for ( int j = 0; j < pSourceBrush->numsides; ++j, ++pSourceSide ) @@ -339,8 +339,8 @@ void CopyMatchingTexinfos( side_t *pDestSides, int numDestSides, const bspbrush_ // This is a hack to allow areaportals to work in water // It was done this way for ease of implementation. // This searches a brush list to find intersecting areaportals and water -// If an areaportal is found inside water, then the water contents and -// texture information is copied over to the areaportal so that the +// If an areaportal is found inside water, then the water contents and +// texture information is copied over to the areaportal so that the // resulting space has the same properties as the water (normal areaportals assume "empty" surroundings) void FixupAreaportalWaterBrushes( bspbrush_t *pList ) { @@ -367,7 +367,7 @@ void FixupAreaportalWaterBrushes( bspbrush_t *pList ) FreeBrush( pIntersect ); pAreaportal->original->contents |= pWater->original->contents; - // HACKHACK: Ideally, this should have been done before the bspbrush_t was + // HACKHACK: Ideally, this should have been done before the bspbrush_t was // created from the map brush. But since it hasn't been, retexture the original map // brush's sides CopyMatchingTexinfos( pAreaportal->sides, pAreaportal->numsides, pWater ); @@ -378,7 +378,7 @@ void FixupAreaportalWaterBrushes( bspbrush_t *pList ) //----------------------------------------------------------------------------- -// MakeBspBrushList +// MakeBspBrushList //----------------------------------------------------------------------------- // UNDONE: Put detail brushes in a separate brush array and pass that instead of "onlyDetail" ? bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, const Vector& clipmins, const Vector& clipmaxs, int detailScreen) @@ -415,7 +415,7 @@ bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, const Vector& clipmi //----------------------------------------------------------------------------- -// A version which uses a passed-in list of brushes +// A version which uses a passed-in list of brushes //----------------------------------------------------------------------------- bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs) { @@ -561,7 +561,7 @@ void WriteBrushVMF(char *name, bspbrush_t *list) BasisForPlane( mapplanes[s->planenum].normal, u, v ); fprintf( f, "\t\t\t\"uaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", u[0], u[1], u[2] ); fprintf( f, "\t\t\t\"vaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", v[0], v[1], v[2] ); - + fprintf( f, "\t\t\t\"rotation\" \"0.0\"\n" ); fprintf( f, "\t\t\t\"lightmapscale\" \"16.0\"\n" ); @@ -594,6 +594,13 @@ void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars ) ADD_CONTENTS(CONTENTS_BLOCKLOS) ADD_CONTENTS(CONTENTS_OPAQUE) ADD_CONTENTS(CONTENTS_TESTFOGVOLUME) +#ifdef MAPBASE + ADD_CONTENTS(CONTENTS_UNUSED) + ADD_CONTENTS(CONTENTS_UNUSED6) + ADD_CONTENTS(CONTENTS_TEAM1) + ADD_CONTENTS(CONTENTS_TEAM2) + ADD_CONTENTS(CONTENTS_IGNORE_NODRAW_OPAQUE) +#endif // MAPBASE ADD_CONTENTS(CONTENTS_MOVEABLE) ADD_CONTENTS(CONTENTS_AREAPORTAL) ADD_CONTENTS(CONTENTS_PLAYERCLIP) @@ -632,14 +639,14 @@ qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2) // Areaportals are allowed to bite water + slime // NOTE: This brush combo should have been fixed up // in a first pass (FixupAreaportalWaterBrushes) - if( (b2->original->contents & MASK_SPLITAREAPORTAL) && + if( (b2->original->contents & MASK_SPLITAREAPORTAL) && (b1->original->contents & CONTENTS_AREAPORTAL) ) { return true; } - + // detail brushes never bite structural brushes - if ( (b1->original->contents & CONTENTS_DETAIL) + if ( (b1->original->contents & CONTENTS_DETAIL) && !(b2->original->contents & CONTENTS_DETAIL) ) return false; if (b1->original->contents & CONTENTS_SOLID) @@ -656,7 +663,7 @@ qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2) ChopBrushes Carves any intersecting solid brushes into the minimum number -of non-intersecting brushes. +of non-intersecting brushes. ================= */ bspbrush_t *ChopBrushes (bspbrush_t *head) diff --git a/utils/vbsp/cubemap.cpp b/utils/vbsp/cubemap.cpp index 829efdfb9..5694b59d8 100644 --- a/utils/vbsp/cubemap.cpp +++ b/utils/vbsp/cubemap.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ //=============================================================================// @@ -40,7 +40,7 @@ bHasEnvMapInMaterial is set if the side's material has $envmap. bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides. - Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't + Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't referenced by some env_cubemap), it does Cubemap_CreateTexInfo. */ @@ -57,8 +57,8 @@ struct CubemapInfo_t }; static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs ) -{ - return ( lhs.m_nTableId < rhs.m_nTableId ); +{ + return ( lhs.m_nTableId < rhs.m_nTableId ); } @@ -84,13 +84,19 @@ inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide ) return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap; } - +#ifdef PARALLAX_CORRECTED_CUBEMAPS +char* g_pParallaxObbStrs[MAX_MAP_CUBEMAPSAMPLES]; +void Cubemap_InsertSample( const Vector& origin, int size, char* pParallaxObbStr = "" ) +{ + g_pParallaxObbStrs[g_nCubemapSamples] = pParallaxObbStr; +#else void Cubemap_InsertSample( const Vector& origin, int size ) { +#endif // PARALLAX_CORRECTED_CUBEMAPS dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples]; - pSample->origin[0] = ( int )origin[0]; - pSample->origin[1] = ( int )origin[1]; - pSample->origin[2] = ( int )origin[2]; + pSample->origin[0] = ( int )origin[0]; + pSample->origin[1] = ( int )origin[1]; + pSample->origin[2] = ( int )origin[2]; pSample->size = size; g_nCubemapSamples++; } @@ -133,7 +139,7 @@ static void ForwardSlashToBackSlash( char *pname ) #define MAX_MATERIAL_NAME 512 // This is the list of materialvars which are used in our codebase to look up dependent materials -static const char *s_pDependentMaterialVar[] = +static const char *s_pDependentMaterialVar[] = { "$bottommaterial", // Used by water materials "$crackmaterial", // Used by shattered glass materials @@ -241,7 +247,7 @@ static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkybo *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags(); int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA ); int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA ); - + // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) || ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) || @@ -277,7 +283,14 @@ void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bH Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) ); } +#ifdef MAPBASE +extern bool g_bSkyboxCubemaps; +extern int g_iDefaultCubemapSize; +#define DEFAULT_CUBEMAP_SIZE g_iDefaultCubemapSize + +#else #define DEFAULT_CUBEMAP_SIZE 32 +#endif // MAPBASE void CreateDefaultCubemaps( bool bHDR ) { @@ -308,7 +321,7 @@ void CreateDefaultCubemaps( bool bHDR ) } Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n" " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName ); - + // Figure out the mip differences between the two textures int iMipLevelOffset = 0; int tmp = pSrcVTFTextures[0]->Width(); @@ -321,7 +334,7 @@ void CreateDefaultCubemaps( bool bHDR ) // Create the destination cubemap IVTFTexture *pDstCubemap = CreateVTFTexture(); pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1, - pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP, + pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP, pSrcVTFTextures[0]->FrameCount() ); // First iterate over all frames @@ -339,9 +352,17 @@ void CreateDefaultCubemaps( bool bHDR ) int iSize = pDstCubemap->ComputeMipSize( iMip ); int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset ); +#ifdef MAPBASE + if (!g_bSkyboxCubemaps) + { + memset( pDstBits, 0, iSize ); + continue; + } +#else // !!! FIXME: Set this to black until HDR cubemaps are built properly! memset( pDstBits, 0, iSize ); continue; +#endif // MAPBASE if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square { @@ -350,10 +371,10 @@ void CreateDefaultCubemaps( bool bHDR ) int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 ); // Replicate 1x1 mip level across entire face - //memset( pDstBits, 0, iSize ); + //memset( pDstBits, 0, iSize ); for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ ) { - memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize ); + memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize ); } } else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square @@ -366,7 +387,7 @@ void CreateDefaultCubemaps( bool bHDR ) else { // Just copy the mip level - memcpy( pDstBits, pSrcBits, iSize ); + memcpy( pDstBits, pSrcBits, iSize ); } } else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide @@ -381,7 +402,7 @@ void CreateDefaultCubemaps( bool bHDR ) else { // Copy row at a time and repeat last row - memcpy( pDstBits, pSrcBits, iSize/2 ); + memcpy( pDstBits, pSrcBits, iSize/2 ); //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 ); int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset ); int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip ); @@ -429,6 +450,14 @@ void CreateDefaultCubemaps( bool bHDR ) pDstCubemap->ConvertImageFormat( originalFormat, false ); } +#ifdef MAPBASE + // Apply a seam fix + pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false ); + + pDstCubemap->MatchCubeMapBorders( 1, IMAGE_FORMAT_RGBA8888, false ); + pDstCubemap->MatchCubeMapBorders( 2, originalFormat, false ); +#endif // MAPBASE + // Write the puppy out! char dstVTFFileName[1024]; if( bHDR ) @@ -471,7 +500,7 @@ void CreateDefaultCubemaps( bool bHDR ) DestroyVTFTexture( pSrcVTFTextures[i] ); } DestroyVTFTexture( pDstCubemap ); -} +} void Cubemap_CreateDefaultCubemaps( void ) { @@ -508,8 +537,8 @@ void Cubemap_SaveBrushSides( const char *pSideListStr ) static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen ) { const char *pSeparator = bMaterialName ? "_" : ""; - int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName, - pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] ); + int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName, + pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] ); if ( bMaterialName ) { @@ -528,12 +557,16 @@ static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &i //----------------------------------------------------------------------------- // Patches the $envmap for a material and all its dependents, returns true if any patching happened //----------------------------------------------------------------------------- +#ifdef PARALLAX_CORRECTED_CUBEMAPS +static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture, const char *pParallaxObbMatrix = "" ) +#else static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture ) +#endif // PARALLAX_CORRECTED_CUBEMAPS { // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap' // FIXME: It's theoretically ok to patch the material if $envmap is not specified, - // because we're using the 'replace' block, which will only add the env_cubemap if + // because we're using the 'replace' block, which will only add the env_cubemap if // $envmap is specified in the source material. But it will fail if someone adds // a specific non-env_cubemap $envmap to the source material at a later point. Bleah @@ -546,7 +579,11 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar ); if ( pDependentMaterial ) { +#ifdef PARALLAX_CORRECTED_CUBEMAPS + bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture, pParallaxObbMatrix ); +#else bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture ); +#endif // PARALLAX_CORRECTED_CUBEMAPS } // If we have neither to patch, we're done @@ -557,7 +594,11 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons char pPatchedMaterialName[1024]; GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + MaterialPatchInfo_t pPatchInfo[7]; +#else MaterialPatchInfo_t pPatchInfo[2]; +#endif // PARALLAX_CORRECTED_CUBEMAPS int nPatchCount = 0; if ( bShouldPatchEnvCubemap ) { @@ -567,6 +608,33 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons ++nPatchCount; } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Parallax cubemap matrix + CUtlVector matRowList; + if (pParallaxObbMatrix[0] != '\0') + { + V_SplitString( pParallaxObbMatrix, ";", matRowList ); + + // Needed for editor + pPatchInfo[nPatchCount].m_pKey = "$envMapParallax"; + pPatchInfo[nPatchCount].m_pValue = "1"; + ++nPatchCount; + + pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB1"; + pPatchInfo[nPatchCount].m_pValue = matRowList[0]; + ++nPatchCount; + pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB2"; + pPatchInfo[nPatchCount].m_pValue = matRowList[1]; + ++nPatchCount; + pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB3"; + pPatchInfo[nPatchCount].m_pValue = matRowList[2]; + ++nPatchCount; + pPatchInfo[nPatchCount].m_pKey = "$envMapOrigin"; + pPatchInfo[nPatchCount].m_pValue = matRowList[3]; + ++nPatchCount; + } +#endif // PARALLAX_CORRECTED_CUBEMAPS + char pDependentPatchedMaterialName[1024]; if ( bDependentMaterialPatched ) { @@ -578,14 +646,21 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons ++nPatchCount; } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_INSERT ); + + // Clean up parallax stuff + matRowList.PurgeAndDeleteElements(); +#else CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE ); +#endif // PARALLAX_CORRECTED_CUBEMAPS return true; } //----------------------------------------------------------------------------- -// Finds a texinfo that has a particular +// Finds a texinfo that has a particular //----------------------------------------------------------------------------- @@ -597,7 +672,11 @@ static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, cons // default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at // runtime before they run buildcubemaps. //----------------------------------------------------------------------------- +#ifdef PARALLAX_CORRECTED_CUBEMAPS +static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3], int cubemapIndex ) +#else static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) +#endif // PARALLAX_CORRECTED_CUBEMAPS { // Don't make cubemap tex infos for nodes if ( originalTexInfo == TEXINFO_NODE ) @@ -629,6 +708,15 @@ static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) char pGeneratedTexDataName[1024]; GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Append origin info if this cubemap has a parallax OBB + char originAppendedString[1024] = ""; + if (g_pParallaxObbStrs[cubemapIndex] && g_pParallaxObbStrs[cubemapIndex][0] != '\0') + { + Q_snprintf(originAppendedString, 1024, "%s;[%d %d %d]", g_pParallaxObbStrs[cubemapIndex], origin[0], origin[1], origin[2]); + } +#endif // PARALLAX_CORRECTED_CUBEMAPS + // Make sure the texdata doesn't already exist. int nTexDataID = FindTexData( pGeneratedTexDataName ); bool bHasTexData = (nTexDataID != -1); @@ -640,9 +728,13 @@ static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) // Hook the texture into the material and all dependent materials // but if no hooking was necessary, exit out +#ifdef PARALLAX_CORRECTED_CUBEMAPS + if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName, originAppendedString ) ) +#else if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) ) +#endif // PARALLAX_CORRECTED_CUBEMAPS return originalTexInfo; - + // Store off the name of the cubemap that we need to create since we successfully patched char pFileName[1024]; int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName ); @@ -656,11 +748,11 @@ static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) } Assert( nTexDataID != -1 ); - + texinfo_t newTexInfo; newTexInfo = *pTexInfo; newTexInfo.texdata = nTexDataID; - + int nTexInfoID = -1; // See if we need to make a new texinfo @@ -670,7 +762,7 @@ static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] ) nTexInfoID = FindTexInfo( newTexInfo ); bHasTexInfo = (nTexInfoID != -1); } - + // Make a new texinfo if we need to. if( !bHasTexInfo ) { @@ -715,12 +807,12 @@ void Cubemap_FixupBrushSidesMaterials( void ) int sideIndex = SideIDToIndex( brushSideID ); if( sideIndex < 0 ) { - Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n", + Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n", g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] ); continue; } - + side_t *pSide = &g_MainMap->brushsides[sideIndex]; #ifdef DEBUG @@ -729,7 +821,7 @@ void Cubemap_FixupBrushSidesMaterials( void ) Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo ); } #endif - + pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin ); if ( pSide->pMapDisp ) { @@ -859,7 +951,7 @@ int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide ) // Look for cubemaps in front of the surface first. for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap ) - { + { dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap]; Vector vecSampleOrigin( static_cast( pSample->origin[0] ), static_cast( pSample->origin[1] ), @@ -879,7 +971,7 @@ int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide ) if( iMinCubemap == -1 ) { for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap ) - { + { dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap]; Vector vecSampleOrigin( static_cast( pSample->origin[0] ), static_cast( pSample->origin[1] ), @@ -945,8 +1037,12 @@ void Cubemap_AttachDefaultCubemapToSpecularSides( void ) { Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo ); } -#endif +#endif +#ifdef PARALLAX_CORRECTED_CUBEMAPS + pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin, cubemapID ); +#else pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin ); +#endif // PARALLAX_CORRECTED_CUBEMAPS if ( pSide->pMapDisp ) { pSide->pMapDisp->face.texinfo = pSide->texinfo; @@ -965,7 +1061,7 @@ void Cubemap_AddUnreferencedCubemaps() for ( i=0; iorigin[1]; info.m_pOrigin[2] = pSample->origin[2]; GeneratePatchedName( "c", info, false, pTextureName, 1024 ); - + // find or add for ( j=0; jmapplanes[pbrush->sides[index].planenum]; winding_t *frontwinding, *backwinding; ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset); - + // only clip if some part of this face is on the back side of all brush sides if ( !backwinding || WindingIsTiny(backwinding)) { @@ -403,7 +403,7 @@ bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList ) // make a face for the fragment face_t *f = NewFaceFromFace( pFace ); f->w = frontwinding; - + // link the fragment in f->next = *pOutputList; *pOutputList = f; @@ -428,7 +428,7 @@ bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList ) //----------------------------------------------------------------------------- -// Purpose: Given an original side and chopped winding, make a face_t +// Purpose: Given an original side and chopped winding, make a face_t // Input : *side - side of the original brush // *winding - winding for this face (portion of the side) // Output : face_t @@ -440,6 +440,9 @@ face_t *MakeBrushFace( side_t *originalSide, winding_t *winding ) f->split[0] = f->split[1] = NULL; f->w = CopyWinding( winding ); f->originalface = originalSide; +#ifdef MAPBASE + f->smoothingGroups = originalSide->smoothingGroups; +#endif // // save material info // @@ -456,7 +459,7 @@ face_t *MakeBrushFace( side_t *originalSide, winding_t *winding ) //----------------------------------------------------------------------------- // Purpose: Chop away sides that are inside other brushes. -// Brushes have already been chopped up so that they do not overlap, +// Brushes have already been chopped up so that they do not overlap, // they merely touch. // Input : *list - list of brushes // Output : face_t * - list of visible faces (some marked bad/split) @@ -620,7 +623,7 @@ face_t *ComputeVisibleBrushSides( bspbrush_t *list ) winding_t *winding = pbrush->sides[i].winding; if ( !winding ) continue; - + if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) ) continue; @@ -663,7 +666,7 @@ face_t *ComputeVisibleBrushSides( bspbrush_t *list ) { // it cut into more than one visible fragment // Don't fragment details - // UNDONE: Build 2d convex hull of this list and swap face winding + // UNDONE: Build 2d convex hull of this list and swap face winding // with that polygon? That would fix the remaining issues. FreeFaceList( pClip ); pClip = NULL; @@ -671,7 +674,7 @@ face_t *ComputeVisibleBrushSides( bspbrush_t *list ) } } } - + // move visible fragments to global face list while ( pFaces ) { diff --git a/utils/vbsp/manifest.cpp b/utils/vbsp/manifest.cpp index c72a95646..0fbcda30a 100644 --- a/utils/vbsp/manifest.cpp +++ b/utils/vbsp/manifest.cpp @@ -5,21 +5,39 @@ #include "manifest.h" #include "windows.h" +#ifdef MAPBASE +entity_t *g_ManifestWorldSpawn = NULL; + +extern char g_MainMapPath[ MAX_PATH ]; +#endif + +//----------------------------------------------------------------------------- +// Purpose: default constructor +//----------------------------------------------------------------------------- +CManifestMapPrefs::CManifestMapPrefs( void ) +{ + m_nInternalId = 0; + m_bIsVisible = true; + //m_bIsPrimary = false; +} + //----------------------------------------------------------------------------- // Purpose: default constructor //----------------------------------------------------------------------------- CManifestMap::CManifestMap( void ) { + m_nInternalId = 0; m_RelativeMapFileName[ 0 ] = 0; m_bTopLevelMap = false; + m_bIsVisible = true; } //----------------------------------------------------------------------------- // Purpose: default constructor //----------------------------------------------------------------------------- -CManifest::CManifest( void ) -{ +CManifest::CManifest( void ) +{ m_InstancePath[ 0 ] = 0; m_bIsCordoning = false; m_CordoningMapEnt = NULL; @@ -35,7 +53,11 @@ CManifest::CManifest( void ) //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap ) { - if ( !stricmp( szKey, "Name" ) ) + if ( !stricmp( szKey, "InternalID" ) ) + { + pManifestMap->m_nInternalId = atoi( szValue ); + } + else if ( !stricmp( szKey, "Name" ) ) { // pManifestMap->m_FriendlyName = szValue; } @@ -101,9 +123,9 @@ ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManif //----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : +// Purpose: +// Input : +// Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon ) { @@ -117,9 +139,9 @@ ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t //----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : +// Purpose: +// Input : +// Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox ) { @@ -137,9 +159,9 @@ ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const //----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : +// Purpose: +// Input : +// Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon ) { @@ -158,9 +180,9 @@ ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const cha //----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : +// Purpose: +// Input : +// Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest ) { @@ -260,6 +282,81 @@ ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFi return( eResult ); } +//----------------------------------------------------------------------------- +// Parses the preferences chunk that pertains to specific submaps in the map: +// +// Maps +// { +// VMF +// { +// "InternalID" "1" +// "IsPrimary" "1" +// } +// VMF +// { +// "InternalID" "2" +// } +// VMF +// { +// "InternalID" "3" +// "IsVisible" "0" +// } +// } +// +//----------------------------------------------------------------------------- + +ChunkFileResult_t CManifest::LoadPrefsVmfKeyCallback( const char *szKey, const char *szValue, CManifestMapPrefs *pManifestMapPrefs ) +{ + if ( !stricmp( szKey, "InternalID" ) ) + { + pManifestMapPrefs->m_nInternalId = atoi( szValue ); + } + else if ( !stricmp( szKey, "IsVisible" ) ) + { + pManifestMapPrefs->m_bIsVisible = ( atoi( szValue ) == 1 ); + } + //else if ( !stricmp( szKey, "IsPrimary" ) ) + //{ + // pManifestMapPrefs->m_bIsPrimary = ( atoi( szValue ) == 1 ); + //} + + return ChunkFile_Ok; +} + +//----------------------------------------------------------------------------- +// Parses preferences and applies them to their corresponding submaps +//----------------------------------------------------------------------------- +ChunkFileResult_t CManifest::LoadPrefsVmfCallback( CChunkFile *pFile, CManifest *pManifest ) +{ + CManifestMapPrefs prefs; + ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadPrefsVmfKeyCallback, &prefs ); + + if (eResult == ChunkFile_Ok && prefs.m_nInternalId != 0) + { + for( int i = 0; i < pManifest->m_Maps.Count(); i++ ) + { + if ( pManifest->m_Maps[ i ]->m_nInternalId == prefs.m_nInternalId ) + { + pManifest->m_Maps[ i ]->m_bIsVisible = prefs.m_bIsVisible; + break; + } + } + } + + return(eResult); +} +ChunkFileResult_t CManifest::LoadPrefsMapsCallback( CChunkFile *pFile, CManifest *pManifest ) +{ + CChunkHandlerMap Handlers; + Handlers.AddHandler( "VMF", ( ChunkHandler_t )CManifest::LoadPrefsVmfCallback, pManifest ); + pFile->PushHandlers(&Handlers); + + ChunkFileResult_t eResult = pFile->ReadChunk(); + + pFile->PopHandlers(); + + return( eResult ); +} //----------------------------------------------------------------------------- // Purpose: this function will create a new entity pair @@ -282,7 +379,7 @@ epair_t *CManifest::CreateEPair( char *pKey, char *pValue ) //----------------------------------------------------------------------------- -// Purpose: this function will load in all of the submaps belonging to this manifest, +// Purpose: this function will load in all of the submaps belonging to this manifest, // except for the top level map, which is loaded separately. // Input : pMapFile - the top level map that was previously loaded // pszFileName - the absolute file name of the top level map file @@ -298,12 +395,20 @@ bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ) memset( InstanceEntity, 0, sizeof( *InstanceEntity ) ); InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f ); + +#ifdef MAPBASE + g_ManifestWorldSpawn = InstanceEntity; +#else pEPair = CreateEPair( "classname", "worldspawn" ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; +#endif // MAPBASE for( int i = 0; i < m_Maps.Count(); i++ ) { + if ( g_bNoHiddenManifestMaps && !m_Maps[ i ]->m_bIsVisible ) + continue; + // if ( m_Maps[ i ]->m_bTopLevelMap == false ) { char FileName[ MAX_PATH ]; @@ -349,9 +454,9 @@ bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ) //----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : +// Purpose: +// Input : +// Output : //----------------------------------------------------------------------------- bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) { @@ -361,7 +466,11 @@ bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) UserNameSize = sizeof( UserName ); if ( GetUserName( UserName, &UserNameSize ) == 0 ) { +#ifdef MAPBASE + strcpy( UserName, "default" ); +#else strcpy( UserPrefsFileName, "default" ); +#endif // MAPBASE } sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName ); @@ -385,6 +494,9 @@ bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) CChunkHandlerMap Handlers; Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this ); + if (g_bNoHiddenManifestMaps) + Handlers.AddHandler( "Maps", ( ChunkHandler_t )CManifest::LoadPrefsMapsCallback, this ); + // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this); File.PushHandlers(&Handlers); @@ -456,11 +568,16 @@ bool CManifest::LoadVMFManifest( const char *pszFileName ) if ( g_MainMap == NULL ) { g_MainMap = g_LoadingMap; +#ifdef MAPBASE + V_ExtractFilePath( pszFileName, g_MainMapPath, sizeof( g_MainMapPath ) ); +#endif // MAPBASE } LoadSubMaps( g_LoadingMap, pszFileName ); LoadVMFManifestUserPrefs( pszFileName ); + + LoadSubMaps( g_LoadingMap, pszFileName ); } return ( eResult == ChunkFile_Ok ); @@ -468,9 +585,9 @@ bool CManifest::LoadVMFManifest( const char *pszFileName ) //----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : +// Purpose: +// Input : +// Output : //----------------------------------------------------------------------------- void CManifest::CordonWorld( ) { diff --git a/utils/vbsp/manifest.h b/utils/vbsp/manifest.h index e7b801e1b..8a289ad65 100644 --- a/utils/vbsp/manifest.h +++ b/utils/vbsp/manifest.h @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // //=====================================================================================// @@ -32,8 +32,19 @@ class CManifestMap { public: CManifestMap( void ); + int m_nInternalId; char m_RelativeMapFileName[ MAX_PATH ]; bool m_bTopLevelMap; + bool m_bIsVisible; +}; + +class CManifestMapPrefs +{ +public: + CManifestMapPrefs( void ); + int m_nInternalId; + bool m_bIsVisible; + //int m_bIsPrimary; }; class CManifest @@ -51,6 +62,9 @@ class CManifest static ChunkFileResult_t LoadCordonsKeyCallback( const char *pszKey, const char *pszValue, CManifest *pManifest ); static ChunkFileResult_t LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest ); static ChunkFileResult_t LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pManifest ); + static ChunkFileResult_t LoadPrefsVmfKeyCallback( const char *szKey, const char *szValue, CManifestMapPrefs *pManifestMapPrefs ); + static ChunkFileResult_t LoadPrefsVmfCallback( CChunkFile *pFile, CManifest *pManifest ); + static ChunkFileResult_t LoadPrefsMapsCallback( CChunkFile *pFile, CManifest *pManifest ); bool LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ); epair_t *CreateEPair( char *pKey, char *pValue ); diff --git a/utils/vbsp/map.cpp b/utils/vbsp/map.cpp index 2221c79ff..5c4e8c281 100644 --- a/utils/vbsp/map.cpp +++ b/utils/vbsp/map.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ //=============================================================================// @@ -16,6 +16,13 @@ #include "fgdlib/fgdlib.h" #include "manifest.h" +#ifdef PARALLAX_CORRECTED_CUBEMAPS +#include "matrixinvert.h" +#endif // PARALLAX_CORRECTED_CUBEMAPS +#ifdef MAPBASE_VSCRIPT +#include "vscript_vbsp.h" +#endif // MAPBASE_VSCRIPT + #ifdef VSVMFIO #include "VmfImport.h" #endif // VSVMFIO @@ -29,7 +36,7 @@ #define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same // as clip epsilon, but it is 0.1f and I - // currently don't know how that number was + // currently don't know how that number was // come to (cab) - this is 0.01 of an inch // for clipping brush solids struct LoadSide_t @@ -46,6 +53,14 @@ struct LoadSide_t extern qboolean onlyents; +#ifdef MAPBASE +extern entity_t *g_ManifestWorldSpawn; + +char g_MainMapPath[ MAX_PATH ]; + +// This is done for the instancing fix +bool g_pParallaxObbsDone[MAX_MAP_CUBEMAPSAMPLES]; +#endif CUtlVector< CMapFile * > g_Maps; CMapFile *g_MainMap = NULL; @@ -140,19 +155,19 @@ PlaneTypeForNormal int PlaneTypeForNormal (Vector& normal) { vec_t ax, ay, az; - -// NOTE: should these have an epsilon around 1.0? + +// NOTE: should these have an epsilon around 1.0? if (normal[0] == 1.0 || normal[0] == -1.0) return PLANE_X; if (normal[1] == 1.0 || normal[1] == -1.0) return PLANE_Y; if (normal[2] == 1.0 || normal[2] == -1.0) return PLANE_Z; - + ax = fabs(normal[0]); ay = fabs(normal[1]); az = fabs(normal[2]); - + if (ax >= ay && ax >= az) return PLANE_ANYX; if (ay >= ax && ay >= az) @@ -385,7 +400,7 @@ int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector & { Vector t1, t2, normal; vec_t dist; - + VectorSubtract (p0, p1, t1); VectorSubtract (p2, p1, t2); CrossProduct (t1, t2, normal); @@ -768,7 +783,7 @@ void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent ) //----------------------------------------------------------------------------- // Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side -// Input : *brush - +// Input : *brush - //----------------------------------------------------------------------------- void RemoveContentsDetailFromBrush( mapbrush_t *brush ) { @@ -791,7 +806,7 @@ void RemoveContentsDetailFromBrush( mapbrush_t *brush ) //----------------------------------------------------------------------------- // Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes -// Input : *mapent - +// Input : *mapent - //----------------------------------------------------------------------------- void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent ) { @@ -806,9 +821,9 @@ void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *pFile - -// *pDisp - +// Purpose: +// Input : *pFile - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) @@ -818,10 +833,10 @@ ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pM //----------------------------------------------------------------------------- -// Purpose: -// Input : szKey - -// szValue - -// pDisp - +// Purpose: +// Input : szKey - +// szValue - +// pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) @@ -900,10 +915,10 @@ ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapD //----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *mapent - +// Purpose: +// Input : *szKey - +// *szValue - +// *mapent - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) @@ -954,9 +969,9 @@ ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue //----------------------------------------------------------------------------- -// Purpose: -// Input : *pFile - -// *pDisp - +// Purpose: +// Input : *pFile - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) @@ -966,10 +981,10 @@ ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMap //----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) @@ -1007,10 +1022,10 @@ ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szVa //----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) @@ -1020,10 +1035,10 @@ ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMap //----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) @@ -1103,10 +1118,10 @@ ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char //----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) @@ -1116,10 +1131,10 @@ ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapD //----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) @@ -1171,7 +1186,7 @@ ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char int nIndex = nRow * nCols; int iTri = nIndex * 2; - while ( pszNext != NULL ) + while ( pszNext != NULL ) { // Collapse the tags here! unsigned short nTriTags = ( unsigned short )atoi( pszNext ); @@ -1212,8 +1227,8 @@ ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char //----------------------------------------------------------------------------- -// Purpose: -// Input : brushSideID - +// Purpose: +// Input : brushSideID - // Output : int //----------------------------------------------------------------------------- int CMapFile::SideIDToIndex( int brushSideID ) @@ -1232,9 +1247,9 @@ int CMapFile::SideIDToIndex( int brushSideID ) //----------------------------------------------------------------------------- -// Purpose: -// Input : *mapent - -// *key - +// Purpose: +// Input : *mapent - +// *key - //----------------------------------------------------------------------------- void ConvertSideList( entity_t *mapent, char *key ) { @@ -1302,7 +1317,7 @@ ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt ) } } while( ( pScan = strtok( NULL, " " ) ) ); } - + // Clear out this entity. pMapEnt->epairs = NULL; return ( ChunkFile_Ok ); @@ -1326,7 +1341,7 @@ static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN ); return ChunkFile_Fail; } - strcpy( pOverlay->szMaterialName, pMaterialName ); + strcpy( pOverlay->szMaterialName, pMaterialName ); } else if ( !stricmp( szKey, "StartU") ) { @@ -1431,7 +1446,7 @@ static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int n //----------------------------------------------------------------------------- // Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs. // These are stored in the object, since the brushes are going to go away. -// Input : *mapent - +// Input : *mapent - //----------------------------------------------------------------------------- void CMapFile::AddLadderKeys( entity_t *mapent ) { @@ -1475,9 +1490,9 @@ ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *pFile - -// ulParam - +// Purpose: +// Input : *pFile - +// ulParam - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) @@ -1495,7 +1510,7 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) mapent->numbrushes = 0; //mapent->portalareas[0] = -1; //mapent->portalareas[1] = -1; - + LoadEntity_t LoadEntity; LoadEntity.pEntity = mapent; @@ -1553,7 +1568,7 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) return(ChunkFile_Ok); } } - + // offset all of the planes and texinfo if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) { @@ -1583,7 +1598,7 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) { MoveBrushesToWorld (mapent); mapent->numbrushes = 0; - + // clear out this entity mapent->epairs = NULL; return(ChunkFile_Ok); @@ -1618,9 +1633,16 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) ) { const char *pSideListStr = ValueForKey( mapent, "sides" ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + char *pParallaxObbStr = ValueForKey( mapent, "parallaxobb" ); +#endif int size; size = IntForKey( mapent, "cubemapsize" ); +#ifdef PARALLAX_CORRECTED_CUBEMAPS + Cubemap_InsertSample( mapent->origin, size, pParallaxObbStr ); +#else Cubemap_InsertSample( mapent->origin, size ); +#endif // PARALLAX_CORRECTED_CUBEMAPS Cubemap_SaveBrushSides( pSideListStr ); } // clear out this entity @@ -1628,6 +1650,88 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) return(ChunkFile_Ok); } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // + // parallax_obb brushes are removed after the transformation matrix is found and saved into + // the entity's data (ent will be removed after data transferred to patched materials) + // + if (!strcmp("parallax_obb", pClassName)) + { + matrix3x4_t obbMatrix, invObbMatrix; + SetIdentityMatrix(obbMatrix); + SetIdentityMatrix(invObbMatrix); + + // Get corner and its 3 edges (scaled, local x, y, and z axes) + mapbrush_t *brush = &mapbrushes[mapent->firstbrush]; + Vector corner, x, y, z; + + // Find first valid winding (with these whiles, if not enough valid windings then identity matrix is passed through to vmts) + int i = 0; + while (i < brush->numsides) + { + winding_t* wind = brush->original_sides[i].winding; + if (!wind) + { + i++; + continue; + } + + corner = wind->p[0]; + y = wind->p[1] - corner; + z = wind->p[3] - corner; + x = CrossProduct(y, z).Normalized(); + + i++; + break; + } + + // Skip second valid winding (opposite face from first, unusable for finding Z's length) + while (i < brush->numsides) + { + winding_t* wind = brush->original_sides[i].winding; + if (!wind) + { + i++; + continue; + } + i++; + break; + } + + // Find third valid winding + while (i < brush->numsides) + { + winding_t* wind = brush->original_sides[i].winding; + if (!wind) + { + i++; + continue; + } + + // Find length of x + // Start with diagonal, then scale x by the projection of diag onto x + Vector diag = wind->p[0] - wind->p[2]; + x *= abs(DotProduct(diag, x)); + + // Build transformation matrix (what is needed to turn a [0,0,0] - [1,1,1] cube into this brush) + MatrixSetColumn(x, 0, obbMatrix); + MatrixSetColumn(y, 1, obbMatrix); + MatrixSetColumn(z, 2, obbMatrix); + MatrixSetColumn(corner, 3, obbMatrix); + + //find inverse (we need the world to local matrix, "transformationmatrix" is kind of a misnomer) + MatrixInversion(obbMatrix, invObbMatrix); + break; + } + + char szMatrix[1024]; + Q_snprintf(szMatrix, 1024, "[%f %f %f %f];[%f %f %f %f];[%f %f %f %f]", invObbMatrix[0][0], invObbMatrix[0][1], invObbMatrix[0][2], invObbMatrix[0][3], invObbMatrix[1][0], invObbMatrix[1][1], invObbMatrix[1][2], invObbMatrix[1][3], invObbMatrix[2][0], invObbMatrix[2][1], invObbMatrix[2][2], invObbMatrix[2][3]); + SetKeyValue(mapent, "transformationmatrix", szMatrix); + + return (ChunkFile_Ok); + } +#endif // PARALLAX_CORRECTED_CUBEMAPS + if ( !strcmp( "test_sidelist", pClassName ) ) { ConvertSideList(mapent, "sides"); @@ -1817,7 +1921,7 @@ void CMapFile::ForceFuncAreaPortalWindowContents() const char *pClassName = ValueForKey( e, "classname" ); // Don't do this on "normal" func_areaportal entities. Those are tied to doors - // and should be opaque when closed. But areaportal windows (and any other + // and should be opaque when closed. But areaportal windows (and any other // distance-based areaportals) should be windows because they are normally open/transparent if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) ) continue; @@ -2001,7 +2105,12 @@ void CMapFile::CheckForInstances( const char *pszFileName ) } char FDGPath[ MAX_PATH ]; +#ifdef MAPBASE + // Mapbase's FGD would be in a MOD path + if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "MOD", FDGPath, sizeof( FDGPath ) ) ) +#else if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) ) +#endif // MAPBASE { if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, NULL, FDGPath, sizeof( FDGPath ) ) ) { @@ -2011,7 +2120,7 @@ void CMapFile::CheckForInstances( const char *pszFileName ) GD.Load( FDGPath ); - // this list will grow as instances are merged onto it. sub-instances are merged and + // this list will grow as instances are merged onto it. sub-instances are merged and // automatically done in this processing. for ( int i = 0; i < num_entities; i++ ) { @@ -2141,7 +2250,7 @@ void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vect { } brush->id += max_brush_id; - + int index = brush->original_sides - Instance->brushsides; brush->original_sides = &brushsides[ nummapbrushsides + index ]; } @@ -2180,7 +2289,7 @@ void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, V // The planes got merged & remapped. So you need to search for the output plane index on each side // NOTE: You could optimize this by saving off an index map in MergePlanes side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist ); - side->id += max_side_id; + side->id += max_side_id; // this could be pre-processed into a list for quicker checking bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 ); @@ -2224,7 +2333,7 @@ void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, V int planenum = side->planenum; cplane_t inPlane, outPlane; inPlane.normal = mapplanes[ planenum ].normal; - inPlane.dist = mapplanes[ planenum ].dist; + inPlane.dist = mapplanes[ planenum ].dist; MatrixTransformPlane( InstanceMatrix, inPlane, outPlane ); planenum = FindFloatPlane( outPlane.normal, outPlane.dist ); @@ -2246,7 +2355,7 @@ void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, V if ( side->pMapDisp ) { mapdispinfo_t *disp = side->pMapDisp; - + disp->brushSideID = side->id; Vector inPoint = disp->startPosition; VectorTransform( inPoint, InstanceMatrix, disp->startPosition ); @@ -2254,7 +2363,7 @@ void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, V disp->face.originalface = side; disp->face.texinfo = side->texinfo; disp->face.planenum = side->planenum; - disp->entitynum += num_entities; + disp->entitynum += num_entities; for( int point = 0; point < disp->face.w->numpoints; point++ ) { @@ -2333,6 +2442,11 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec entity_t *WorldspawnEnt = NULL; GameData::TNameFixup FixupStyle; +#ifdef MAPBASE + // For fixing AI node problems with manifests and instances + int max_ai_node_id = 0; +#endif + char *pTargetName = ValueForKey( pInstanceEntity, "targetname" ); char *pName = ValueForKey( pInstanceEntity, "name" ); if ( pTargetName[ 0 ] ) @@ -2359,6 +2473,20 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec max_entity_id = value; } } + +#ifdef MAPBASE + // If this is a classname starting with "info_node", look for a node ID keyvalue and + // add it to the counter. + if ( strnicmp( ValueForKey( &entities[ i ], "classname" ), "info_node", 9 ) == 0 ) + { + int value = atoi( ValueForKey( &entities[i], "nodeid" ) ); + if ( value > max_ai_node_id ) + { + max_ai_node_id = value; + //Warning( "Max AI nodes is now %i", max_ai_node_id ); + } + } +#endif // MAPBASE } FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) ); @@ -2403,6 +2531,10 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle ); if ( EntClass ) { +#ifdef MAPBASE + // Sets up for additional instance remap fixes from Mapbase + GD.SetupInstanceRemapParams( max_ai_node_id, nummapbrushsides - Instance->nummapbrushsides, IntForKey( pInstanceEntity, "remap_vecline" ) > 0 ); +#endif // MAPBASE for( int i = 0; i < EntClass->GetVariableCount(); i++ ) { GDinputvariable *EntVar = EntClass->GetVariableAt( i ); @@ -2442,13 +2574,59 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z ); SetKeyValue( entity, "normal.z", temp );*/ } +#ifdef MAPBASE + else if ( !strcmp( pEntity, "func_instance" ) ) + { + int iNumReplaces = 0; + for ( epair_t *epSubInstance = entity->epairs; epSubInstance != NULL; epSubInstance = epSubInstance->next ) + { + if ( strnicmp( epSubInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 ) + { + iNumReplaces++; + } + } + + // Merge this instance's keys + for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next ) + { + if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 ) + { + iNumReplaces++; + char szKey[32]; + Q_snprintf(szKey, sizeof(szKey), "replace%i", iNumReplaces); + SetKeyValue( entity, szKey, epInstance->value ); + } + } + + // If the parent instance is within a relative path and no file relative to the main map exists, change it to be relative to the parent + char *pParentInstanceFile = ValueForKey( pInstanceEntity, "file" ); + if ( pParentInstanceFile[ 0 ] && (strchr( pParentInstanceFile, '\\' ) || strchr( pParentInstanceFile, '/' )) ) + { + char *pInstanceFile = ValueForKey( entity, "file" ); + if ( pInstanceFile[ 0 ] ) + { + char InstancePath[ MAX_PATH ]; + + if ( !DeterminePath( g_MainMapPath, pInstanceFile, InstancePath ) ) + { + strcpy( InstancePath, pParentInstanceFile ); + V_StripFilename( InstancePath ); + V_strncat( InstancePath, "\\", sizeof( InstancePath ) ); + V_strncat( InstancePath, pInstanceFile, sizeof( InstancePath ) ); + + SetKeyValue( entity, "file", InstancePath ); + } + } + } + } +#endif // MAPBASE } #ifdef MERGE_INSTANCE_DEBUG_INFO Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i ); Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush ); Msg( " KV Pairs:\n" ); - for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next ) + for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next ) { Msg( " %s %s\n", ep->key, ep->value ); } @@ -2496,6 +2674,13 @@ void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vec MoveBrushesToWorldGeneral( WorldspawnEnt ); WorldspawnEnt->numbrushes = 0; +#ifdef MAPBASE + char *pIsTopLevel = ValueForKey( pInstanceEntity, "toplevel" ); + if ( strcmp( pIsTopLevel, "1" ) == 0 ) + { + g_ManifestWorldSpawn->epairs = WorldspawnEnt->epairs; + } +#endif // MAPBASE WorldspawnEnt->epairs = NULL; } @@ -2519,6 +2704,14 @@ void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vec for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ ) { Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); + +#ifdef MAPBASE + int iSides = (nummapbrushsides - Instance->nummapbrushsides); + for (int i2 = 0; i2 < g_aMapWaterOverlays[i].aSideList.Count(); i2++) + { + g_aMapWaterOverlays[i].aSideList[i2] += iSides; + } +#endif // MAPBASE } } @@ -2533,7 +2726,7 @@ bool LoadMapFile( const char *pszFileName ) bool bLoadingManifest = false; CManifest *pMainManifest = NULL; ChunkFileResult_t eResult; - + // // Dummy this up for the texture handling. This can be removed when old .MAP file // support is removed. @@ -2573,6 +2766,9 @@ bool LoadMapFile( const char *pszFileName ) if ( g_MainMap == NULL ) { g_MainMap = g_LoadingMap; +#ifdef MAPBASE + V_ExtractFilePath( pszFileName, g_MainMapPath, sizeof( g_MainMapPath ) ); +#endif // MAPBASE } if ( g_MainMap == g_LoadingMap || verbose ) @@ -2611,6 +2807,20 @@ bool LoadMapFile( const char *pszFileName ) if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF)) { +#ifdef MAPBASE_VSCRIPT + if ( g_pScriptVM ) + { + if (CMapFile::g_Hook_OnMapLoaded.CanRunInScope( NULL )) + { + // Use GetLoadingMap() + //g_pScriptVM->SetValue( "map", g_LoadingMap->GetScriptInstance() ); + + CMapFile::g_Hook_OnMapLoaded.Call( NULL, NULL, NULL ); + + //g_pScriptVM->ClearValue( "map" ); + } + } +#endif // MAPBASE_VSCRIPT // Update the overlay/side list(s). Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays ); OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays ); @@ -2622,6 +2832,51 @@ bool LoadMapFile( const char *pszFileName ) pMainManifest->CordonWorld(); } +#ifdef PARALLAX_CORRECTED_CUBEMAPS + // Fill out parallax obb matrix array + // "i" is static so this code could account for + // multiple LoadMapFile() calls from instances, etc. + for (int i = 0; i < g_nCubemapSamples; i++) + { + if (g_pParallaxObbStrs[i][0] != '\0' && g_pParallaxObbsDone[i] == false) + { + //Warning( "Testing OBB string %s\n", g_pParallaxObbStrs[i] ); + + entity_t* obbEnt = NULL; + for (int i2 = 0; i2 < g_LoadingMap->num_entities; i2++) + { + if (stricmp( ValueForKey( &g_LoadingMap->entities[i2], "targetname" ), g_pParallaxObbStrs[i] ) != 0) + continue; + + obbEnt = &g_LoadingMap->entities[i2]; + g_pParallaxObbStrs[i] = ValueForKey(obbEnt, "transformationmatrix"); + //Warning( "Using OBB transformation matrix \"%s\"\n", g_pParallaxObbStrs[i] ); + g_pParallaxObbsDone[i] = true; + + break; + } + + if (!obbEnt) + { + Warning( "Cannot find parallax obb \"%s\" (num_entities is %i)\n", g_pParallaxObbStrs[i], g_LoadingMap->num_entities ); + //g_pParallaxObbStrs[i][0] = '\0'; + } + } + } + + // Remove parallax_obb entities (in a nice slow linear search) + for (int i = 0; i < g_LoadingMap->num_entities; i++) + { + entity_t* mapent = &g_LoadingMap->entities[i]; + const char *pClassName = ValueForKey( mapent, "classname" ); + if ( !strcmp( "parallax_obb", pClassName ) ) + { + mapent->numbrushes = 0; + mapent->epairs = NULL; + } + } +#endif // PARALLAX_CORRECTED_CUBEMAPS + ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs); for (int i=0 ; ientities[0].numbrushes ; i++) { @@ -2647,7 +2902,7 @@ bool LoadMapFile( const char *pszFileName ) g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]); //TestExpandBrushes(); - + // Clear the error reporting g_MapError.ClearState(); } @@ -2668,9 +2923,9 @@ ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo) } //----------------------------------------------------------------------------- -// Purpose: -// Input : pFile - -// pParent - +// Purpose: +// Input : pFile - +// pParent - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo) @@ -2717,7 +2972,7 @@ ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSid { side->contents &= ~CONTENTS_DETAIL; } - + if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) ) { side->contents |= CONTENTS_SOLID; @@ -2765,7 +3020,7 @@ ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSid { side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin); } - + // save the td off in case there is an origin brush and we // have to recalculate the texinfo if (nummapbrushsides == MAX_MAP_BRUSHSIDES) @@ -2797,11 +3052,11 @@ ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSid //----------------------------------------------------------------------------- -// Purpose: -// Input : szKey - -// szValue - -// pSideInfo - -// Output : +// Purpose: +// Input : szKey - +// szValue - +// pSideInfo - +// Output : //----------------------------------------------------------------------------- ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo) { @@ -2858,7 +3113,7 @@ ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, Lo if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f) { g_MapError.ReportWarning("luxel size of 0"); - pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize; + pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize; } pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale; if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale) @@ -2927,7 +3182,7 @@ ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const strcpy(pOutput->value, szValue); m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs ); - + // // Append it to the end of epairs list. // @@ -2956,9 +3211,9 @@ ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity }; //----------------------------------------------------------------------------- -// Purpose: -// Input : pFile - -// pParent - +// Purpose: +// Input : pFile - +// pParent - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) @@ -2997,7 +3252,7 @@ ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *p // get the content for the entire brush b->contents = BrushContents (b); - // allow detail brushes to be removed + // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) ) { b->numsides = 0; @@ -3080,7 +3335,7 @@ ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *p if( HasDispInfo( b ) ) { // add the base face data to the displacement surface - DispGetFaceInfo( b ); + DispGetFaceInfo( b ); // don't keep this brush b->numsides = 0; @@ -3091,7 +3346,7 @@ ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *p AddBrushBevels (b); nummapbrushes++; - pLoadEntity->pEntity->numbrushes++; + pLoadEntity->pEntity->numbrushes++; } else { @@ -3103,9 +3358,9 @@ ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *p //----------------------------------------------------------------------------- -// Purpose: -// Input : pFile - -// parent - +// Purpose: +// Input : pFile - +// parent - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush) @@ -3119,6 +3374,132 @@ ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, m return ChunkFile_Ok; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HSCRIPT CMapFile::GetScriptInstance() +{ + if (!m_hScriptInstance) + m_hScriptInstance = g_pScriptVM->RegisterInstance( this ); + + return m_hScriptInstance; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapFile::ScriptGetEntityKeyValues( int idx, HSCRIPT hKeyTable, HSCRIPT hValTable ) +{ + epair_t *curPair = entities[idx].epairs; + while (curPair) + { + g_pScriptVM->ArrayAppend( hKeyTable, curPair->key ); + g_pScriptVM->ArrayAppend( hValTable, curPair->value ); + + curPair = curPair->next; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CMapFile::ScriptAddSimpleEntityKV( HSCRIPT hKV ) +{ + if (num_entities == MAX_MAP_ENTITIES) + { + // Exits. + g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); + return -1; + } + + entity_t *mapent = NULL; + if (::num_entities > 0) + { + // We're not loading maps anymore. Add this to the central BSP + mapent = &::entities[num_entities]; + ::num_entities++; + } + else + { + mapent = &entities[num_entities]; + num_entities++; + } + + memset(mapent, 0, sizeof(*mapent)); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + //mapent->portalareas[0] = -1; + //mapent->portalareas[1] = -1; + + LoadEntity_t LoadEntity; + LoadEntity.pEntity = mapent; + + // No default flags/contents + LoadEntity.nBaseFlags = 0; + LoadEntity.nBaseContents = 0; + + int nIterator = -1; + ScriptVariant_t varKey, varValue; + char szValue[256]; + while ((nIterator = g_pScriptVM->GetKeyValue( hKV, nIterator, &varKey, &varValue )) != -1) + { + switch (varValue.m_type) + { + case FIELD_CSTRING: Q_strncpy( szValue, varValue.m_pszString, sizeof(szValue) ); break; + case FIELD_INTEGER: Q_snprintf( szValue, sizeof(szValue), "%i", varValue.m_int ); break; + case FIELD_FLOAT: Q_snprintf( szValue, sizeof(szValue), "%f", varValue.m_float ); break; + case FIELD_CHARACTER: Q_snprintf( szValue, sizeof( szValue ), "%c", varValue.m_char ); break; + case FIELD_BOOLEAN: Q_snprintf( szValue, sizeof(szValue), "%d", varValue.m_bool ); break; + case FIELD_VECTOR: Q_snprintf( szValue, sizeof(szValue), "%f %f %f", (*varValue.m_pVector).x, (*varValue.m_pVector).y, (*varValue.m_pVector).z ); break; + default: szValue[0] = '\0'; break; + } + + LoadEntityKeyCallback( varKey, szValue, &LoadEntity ); + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + + return num_entities - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CMapFile::ScriptAddInstance( const char *pszVMF, const Vector& vecOrigin, const QAngle& angAngles ) +{ + if (num_entities == MAX_MAP_ENTITIES) + { + // Exits. + g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); + return -1; + } + + entity_t *mapent = &entities[num_entities]; + num_entities++; + memset(mapent, 0, sizeof(*mapent)); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + //mapent->portalareas[0] = -1; + //mapent->portalareas[1] = -1; + + SetKeyValue( mapent, "classname", "func_instance" ); + + SetKeyValue( mapent, "file", pszVMF ); + SetKeyValue( mapent, "fixup_style", "2" ); // No fixup + + char szValue[256]; + Q_snprintf( szValue, sizeof(szValue), "%f %f %f", vecOrigin.x, vecOrigin.y, vecOrigin.z ); + SetKeyValue( mapent, "origin", szValue ); + + Q_snprintf( szValue, sizeof(szValue), "%f %f %f", angAngles.x, angAngles.y, angAngles.z ); + SetKeyValue( mapent, "angles", szValue ); + + return num_entities - 1; +} +#endif // MAPBASE_VSCRIPT + /* ================ @@ -3163,7 +3544,7 @@ void CMapFile::TestExpandBrushes (void) fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]); fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]); - fprintf (f, "%s [ 0 0 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n", + fprintf (f, "%s [ 0 0 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n", TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) ); FreeWinding (w); @@ -3296,7 +3677,7 @@ mapdispinfo_t *ParseDispInfoChunk( void ) GetToken( true ); if( strcmp( token, "}" ) ) g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" ); - + // return the index of the displacement info slot return pMapDispInfo; } diff --git a/utils/vbsp/matrixinvert.h b/utils/vbsp/matrixinvert.h new file mode 100644 index 000000000..b3ac3eff5 --- /dev/null +++ b/utils/vbsp/matrixinvert.h @@ -0,0 +1,117 @@ +// By Jason Yu-Tseh Chi +// From http://chi3x10.wordpress.com/2008/05/28/calculate-matrix-inversion-in-c/ +// Modified to work with valve's matrix_3x4_t + +#include "mathlib\mathlib.h" + +// Calculate the cofactor of element (row,col) +int GetMatrixMinor(float **src, float **dest, int row, int col, int order) +{ + // Indicate which col and row is being copied to dest + int colCount = 0, rowCount = 0; + + for (int i = 0; i < order; i++) + { + if (i != row) + { + colCount = 0; + for (int j = 0; j < order; j++) + { + // When j is not the element + if (j != col) + { + dest[rowCount][colCount] = src[i][j]; + colCount++; + } + } + rowCount++; + } + } + + return 1; +} + +// Calculate the determinant recursively. +double CalcMatrixDeterminant(float **mat, int order) +{ + // Order must be >= 0 + // Stop the recursion when matrix is a single element + if (order == 1) + return mat[0][0]; + + // The determinant value + float det = 0; + + // Allocate the cofactor matrix + float **minor; + minor = new float*[order - 1]; + for (int i = 0; i c_peak_portals) c_peak_portals = c_active_portals; - + p = (portal_t*)malloc (sizeof(portal_t)); memset (p, 0, sizeof(portal_t)); p->id = s_PortalCount; @@ -155,7 +155,7 @@ qboolean Portal_EntityFlood (portal_t *p, int s) || p->nodes[1]->planenum != PLANENUM_LEAF) Error ("Portal_EntityFlood: not a leaf"); - // can never cross to a solid + // can never cross to a solid if ( (p->nodes[0]->contents & CONTENTS_SOLID) || (p->nodes[1]->contents & CONTENTS_SOLID) ) return false; @@ -196,7 +196,7 @@ void AddPortalToNodes (portal_t *p, node_t *front, node_t *back) p->nodes[0] = front; p->next[0] = front->portals; front->portals = p; - + p->nodes[1] = back; p->next[1] = back->portals; back->portals = p; @@ -211,14 +211,14 @@ RemovePortalFromNode void RemovePortalFromNode (portal_t *portal, node_t *l) { portal_t **pp, *t; - + // remove reference to the current portal pp = &l->portals; while (1) { t = *pp; if (!t) - Error ("RemovePortalFromNode: portal not in leaf"); + Error ("RemovePortalFromNode: portal not in leaf"); if ( t == portal ) break; @@ -230,7 +230,7 @@ void RemovePortalFromNode (portal_t *portal, node_t *l) else Error ("RemovePortalFromNode: portal not bounding leaf"); } - + if (portal->nodes[0] == l) { *pp = portal->next[0]; @@ -238,7 +238,7 @@ void RemovePortalFromNode (portal_t *portal, node_t *l) } else if (portal->nodes[1] == l) { - *pp = portal->next[1]; + *pp = portal->next[1]; portal->nodes[1] = NULL; } } @@ -249,7 +249,7 @@ void PrintPortal (portal_t *p) { int i; winding_t *w; - + w = p->winding; for (i=0 ; inumpoints ; i++) Msg ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0] @@ -293,7 +293,7 @@ void MakeHeadnodePortals (tree_t *tree) bounds[0][i] = tree->mins[i] - SIDESPACE; bounds[1][i] = tree->maxs[i] + SIDESPACE; } - + tree->outside_node.planenum = PLANENUM_LEAF; tree->outside_node.brushlist = NULL; tree->outside_node.portals = NULL; @@ -306,7 +306,7 @@ void MakeHeadnodePortals (tree_t *tree) p = AllocPortal (); portals[n] = p; - + pl = &bplanes[n]; memset (pl, 0, sizeof(*pl)); if (j) @@ -323,7 +323,7 @@ void MakeHeadnodePortals (tree_t *tree) p->winding = BaseWindingForPlane (pl->normal, pl->dist); AddPortalToNodes (p, node, &tree->outside_node); } - + // clip the basewindings by all the other planes for (i=0 ; i<6 ; i++) { @@ -400,7 +400,7 @@ void MakeNodePortal (node_t *node) w = BaseWindingForNode (node); // clip the portal by all the other portals in the node - for (p = node->portals ; p && w; p = p->next[side]) + for (p = node->portals ; p && w; p = p->next[side]) { if (p->nodes[0] == node) { @@ -438,7 +438,7 @@ void MakeNodePortal (node_t *node) new_portal = AllocPortal (); new_portal->plane = g_MainMap->mapplanes[node->planenum]; new_portal->onnode = node; - new_portal->winding = w; + new_portal->winding = w; AddPortalToNodes (new_portal, node->children[0], node->children[1]); } @@ -464,7 +464,7 @@ void SplitNodePortals (node_t *node) f = node->children[0]; b = node->children[1]; - for (p = node->portals ; p ; p = next_portal) + for (p = node->portals ; p ; p = next_portal) { if (p->nodes[0] == node) side = 0; @@ -521,7 +521,7 @@ void SplitNodePortals (node_t *node) AddPortalToNodes (p, other_node, f); continue; } - + // the winding is split new_portal = AllocPortal (); *new_portal = *p; @@ -558,7 +558,7 @@ void CalcNodeBounds (node_t *node) // calc mins/maxs for both leafs and nodes ClearBounds (node->mins, node->maxs); - for (p = node->portals ; p ; p = p->next[s]) + for (p = node->portals ; p ; p = p->next[s]) { s = (p->nodes[1] == node); for (i=0 ; iwinding->numpoints ; i++) @@ -632,8 +632,8 @@ FLOOD ENTITIES // Purpose: Floods outward from the given node, marking visited nodes with // the number of hops from a node with an entity. If we ever mark // the outside_node for this tree, we've leaked. -// Input : node - -// dist - +// Input : node - +// dist - //----------------------------------------------------------------------------- void FloodPortals_r (node_t *node, int dist) { @@ -700,9 +700,9 @@ void FloodAreaLeak( node_t *headnode, node_t *pFirstSide ) // BSP tree that the entity occupies. // // We then flood outward from that leaf to see if the entity leaks. -// Input : headnode - -// origin - -// occupant - +// Input : headnode - +// origin - +// occupant - // Output : Returns false if the entity is in solid, true if it is not. //----------------------------------------------------------------------------- qboolean PlaceOccupant (node_t *headnode, Vector& origin, entity_t *occupant) @@ -852,7 +852,7 @@ void FloodAreas_r (node_t *node, portal_t *pSeeThrough) Warning("WARNING: areaportal entity %i (brush %i) touches > 2 areas\n", b->original->entitynum, b->original->id ); return; } - + if (e->portalareas[0]) { e->portalareas[1] = c_areas; @@ -920,6 +920,9 @@ void FindAreas_r (node_t *node) FloodAreas_r (node, NULL); } +#ifdef MAPBASE +extern qboolean noleaktest; +#endif void ReportAreaportalLeak( tree_t *tree, node_t *node ) { @@ -968,6 +971,14 @@ void ReportAreaportalLeak( tree_t *tree, node_t *node ) AreaportalLeakFile( tree, pStart, pBest, pBest->nodes[s] ); } } + +#ifdef MAPBASE + if (!noleaktest) + { + Warning( ("--- AREAPORTAL LEAK ---\n") ); + exit(0); + } +#endif } @@ -1042,7 +1053,7 @@ int FindUniquePoints( const Vector2D *pPoints, int nPoints, int *indexMap, int n indexMap[nUniquePoints++] = i; } } - + return nUniquePoints; } @@ -1087,7 +1098,7 @@ int Convex2D( Vector2D const *pPoints, int nPoints, int *indices, int nMaxIndice Vector2D const *pStartPoint = &pPoints[ indices[nIndices-1] ]; float flEdgeAngle = atan2( curEdge.y, curEdge.x ); - + int iMinAngle = -1; float flMinAngle = 5000; @@ -1151,15 +1162,15 @@ int Convex2D( Vector2D const *pPoints, int nPoints, int *indices, int nMaxIndice curEdge = pPoints[indices[nIndices-1]] - pPoints[indices[nIndices-2]]; } - + return nIndices; } -void FindPortalsLeadingToArea_R( - node_t *pHeadNode, - int iSrcArea, - int iDestArea, - plane_t *pPlane, +void FindPortalsLeadingToArea_R( + node_t *pHeadNode, + int iSrcArea, + int iDestArea, + plane_t *pPlane, CUtlVector &portals ) { if (pHeadNode->planenum != PLANENUM_LEAF) @@ -1177,7 +1188,7 @@ void FindPortalsLeadingToArea_R( if( !p->nodes[0]->occupied || !p->nodes[1]->occupied ) continue; - + if( p->nodes[1]->area == iDestArea && p->nodes[0]->area == iSrcArea || p->nodes[0]->area == iDestArea && p->nodes[1]->area == iSrcArea ) { @@ -1203,10 +1214,10 @@ void EmitClipPortalGeometry( node_t *pHeadNode, portal_t *pPortal, int iSrcArea, { // Build a list of all the points in portals from the same original face. CUtlVector portals; - FindPortalsLeadingToArea_R( - pHeadNode, - iSrcArea, - dp->otherarea, + FindPortalsLeadingToArea_R( + pHeadNode, + iSrcArea, + dp->otherarea, &pPortal->plane, portals ); @@ -1260,7 +1271,7 @@ void EmitClipPortalGeometry( node_t *pHeadNode, portal_t *pPortal, int iSrcArea, Vector *p = pPortal->winding->p; Error( "MAX_MAP_PORTALVERTS (probably a broken areaportal near %.1f %.1f %.1f ", p->x, p->y, p->z ); } - + for( i=0; i < nIndices; i++ ) { g_ClipPortalVerts[g_nClipPortalVerts] = points[ indices[i] ]; @@ -1285,7 +1296,7 @@ void SetNodeAreaIndices_R( node_t *node ) if( node->children[0]->area == node->children[1]->area ) node->area = node->children[0]->area; else - node->area = -1; + node->area = -1; } @@ -1316,7 +1327,7 @@ void EmitAreaPortals (node_t *headnode) e = &entities[j]; if (!e->areaportalnum) continue; - + if (e->portalareas[0] == iSrcArea || e->portalareas[1] == iSrcArea) { int iSide = (e->portalareas[0] == iSrcArea); diff --git a/utils/vbsp/staticprop.cpp b/utils/vbsp/staticprop.cpp index 3ba6b95ac..4b8c5b3b0 100644 --- a/utils/vbsp/staticprop.cpp +++ b/utils/vbsp/staticprop.cpp @@ -57,7 +57,7 @@ struct StaticPropBuild_t int m_LightmapResolutionX; int m_LightmapResolutionY; }; - + //----------------------------------------------------------------------------- // Used to cache collision model generation @@ -104,6 +104,7 @@ isstaticprop_ret IsStaticProp( studiohdr_t* pHdr ) if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP)) return RET_FAIL_NOT_MARKED_STATIC_PROP; +#ifndef MAPBASE // If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static KeyValues *modelKeyValues = new KeyValues(pHdr->pszName()); if ( StudioKeyValues( pHdr, modelKeyValues ) ) @@ -119,6 +120,7 @@ isstaticprop_ret IsStaticProp( studiohdr_t* pHdr ) } } modelKeyValues->deleteThis(); +#endif // MAPBASE return RET_VALID; } @@ -311,7 +313,7 @@ static CPhysCollide* GetCollisionModel( char const* pModelName ) // Tests a single leaf against the static prop //----------------------------------------------------------------------------- -static bool TestLeafAgainstCollide( int depth, int* pNodeList, +static bool TestLeafAgainstCollide( int depth, int* pNodeList, Vector const& origin, QAngle const& angles, CPhysCollide* pCollide ) { // Copy the planes in the node list into a list of planes @@ -408,9 +410,9 @@ static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList, pNodeList[depth] = node; ++depth; - ComputeConvexHullLeaves_R( pNode->children[1], + ComputeConvexHullLeaves_R( pNode->children[1], depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList ); - + pNodeList[depth - 1] = - node - 1; ComputeConvexHullLeaves_R( pNode->children[0], depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList ); @@ -434,7 +436,7 @@ static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList, // Places Static Props in the level //----------------------------------------------------------------------------- -static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin, +static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin, QAngle const& angles, CUtlVector& leafList ) { // Compute an axis-aligned bounding box for the collide @@ -492,7 +494,7 @@ static void AddStaticPropToLump( StaticPropBuild_t const& build ) // Insert an element into the lump data... int i = s_StaticPropLump.AddToTail( ); StaticPropLump_t& propLump = s_StaticPropLump[i]; - propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName ); + propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName ); VectorCopy( build.m_Origin, propLump.m_Origin ); VectorCopy( build.m_Angles, propLump.m_Angles ); propLump.m_FirstLeaf = s_StaticPropLeafLump.Count(); @@ -509,7 +511,7 @@ static void AddStaticPropToLump( StaticPropBuild_t const& build ) propLump.m_flForcedFadeScale = build.m_flForcedFadeScale; propLump.m_nMinDXLevel = build.m_nMinDXLevel; propLump.m_nMaxDXLevel = build.m_nMaxDXLevel; - + if (build.m_pLightingOrigin && *build.m_pLightingOrigin) { if (ComputeLightingOrigin( build, propLump.m_LightingOrigin )) @@ -569,6 +571,10 @@ static void SetLumpData( ) void EmitStaticProps() { +#ifdef MAPBASE + Msg("Placing static props...\n"); +#endif // MAPBASE + CreateInterfaceFn physicsFactory = GetPhysicsFactory(); if ( physicsFactory ) { @@ -592,13 +598,43 @@ void EmitStaticProps() for ( i = 0; i < num_entities; ++i) { char* pEntity = ValueForKey(&entities[i], "classname"); +#ifdef MAPBASE + const int iInsertAsStatic = IntForKey( &entities[i], "insertasstaticprop" ); // If the key is absent, IntForKey will return 0. + bool bInsertAsStatic = g_bPropperInsertAllAsStatic; + + // 1 = No, 2 = Yes; Any other number will just use what g_bPropperInsertAllAsStatic is set as. + if ( iInsertAsStatic == 1 ) { bInsertAsStatic = false; } + else if ( iInsertAsStatic == 2 ) { bInsertAsStatic = true; } + + if ( !strcmp( pEntity, "static_prop" ) || !strcmp( pEntity, "prop_static" ) || ( !strcmp( pEntity, "propper_model" ) && bInsertAsStatic ) ) +#else if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static")) +#endif // MAPBASE { StaticPropBuild_t build; GetVectorForKey( &entities[i], "origin", build.m_Origin ); GetAnglesForKey( &entities[i], "angles", build.m_Angles ); +#ifdef MAPBASE + if ( !strcmp( pEntity, "propper_model" ) ) + { + char* pModelName = ValueForKey( &entities[i], "modelname" ); + + // The modelname keyvalue lacks 'models/' at the start and '.mdl' at the end, so we have to add them. + char modelpath[MAX_VALUE]; + sprintf( modelpath, "models/%s.mdl", pModelName ); + + Msg( "Inserting propper_model (%.0f %.0f %.0f) as prop_static: %s\n", build.m_Origin[0], build.m_Origin[1], build.m_Origin[2], modelpath ); + + build.m_pModelName = modelpath; + } + else // Otherwise we just assume it's a normal prop_static + { + build.m_pModelName = ValueForKey( &entities[i], "model" ); + } +#else build.m_pModelName = ValueForKey( &entities[i], "model" ); +#endif // MAPBASE build.m_Solid = IntForKey( &entities[i], "solid" ); build.m_Skin = IntForKey( &entities[i], "skin" ); build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" ); @@ -627,7 +663,7 @@ void EmitStaticProps() if (IntForKey( &entities[i], "generatelightmaps") == 0) { - build.m_Flags |= STATIC_PROP_NO_PER_TEXEL_LIGHTING; + build.m_Flags |= STATIC_PROP_NO_PER_TEXEL_LIGHTING; build.m_LightmapResolutionX = 0; build.m_LightmapResolutionY = 0; } @@ -649,11 +685,11 @@ void EmitStaticProps() build.m_FadesOut = (build.m_FadeMaxDist > 0); build.m_pLightingOrigin = ValueForKey( &entities[i], "lightingorigin" ); if (build.m_FadesOut) - { + { build.m_FadeMinDist = FloatForKey( &entities[i], "fademindist" ); if (build.m_FadeMinDist < 0) { - build.m_FadeMinDist = build.m_FadeMaxDist; + build.m_FadeMinDist = build.m_FadeMaxDist; } } else @@ -667,6 +703,13 @@ void EmitStaticProps() // strip this ent from the .bsp file entities[i].epairs = 0; } +#ifdef MAPBASE + else if ( g_bPropperStripEntities && !strncmp( pEntity, "propper_", 8 ) ) // Strip out any entities with 'propper_' in their classname, as they don't actually exist in-game. + { + Warning( "Not including %s in BSP compile due to it being a propper entity that isn't used in-game.\n", pEntity ); + entities[i].epairs = 0; + } +#endif // MAPBASE } // Strip out lighting origins; has to be done here because they are used when @@ -715,7 +758,7 @@ const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData ) // mandatory callback to make requested data resident // load and persist the vertex file - strcpy( fileName, "models/" ); + strcpy( fileName, "models/" ); strcat( fileName, g_pActiveStudioHdr->pszName() ); Q_StripExtension( fileName, fileName, sizeof( fileName ) ); strcat( fileName, ".vvd" ); diff --git a/utils/vbsp/textures.cpp b/utils/vbsp/textures.cpp index 4f49c5d47..fb9db5272 100644 --- a/utils/vbsp/textures.cpp +++ b/utils/vbsp/textures.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -54,7 +54,7 @@ int FindMiptex (const char *name) const char *propVal, *propVal2; int opacity; bool found; - + for (i=0 ; inormal, baseaxis[i*3]); @@ -331,7 +338,7 @@ void TextureAxisFromPlane(plane_t *pln, Vector& xv, Vector& yv) bestaxis = i; } } - + VectorCopy (baseaxis[bestaxis*3+1], xv); VectorCopy (baseaxis[bestaxis*3+2], yv); } @@ -438,7 +445,7 @@ int FindAliasedTexData( const char *pName_, dtexdata_t *sourceTexture ) GetMaterialDimensions( matID, &pTexData->width, &pTexData->height ); pTexData->view_width = pTexData->width; // undone: what is this? pTexData->view_height = pTexData->height; // undone: what is this? - + GetMaterialReflectivity( matID, pTexData->reflectivity.Base() ); g_SurfaceProperties[output] = GetSurfaceProperties( matID, pName ); @@ -501,12 +508,12 @@ int FindOrCreateTexData( const char *pName_ ) GetMaterialDimensions( matID, &pTexData->width, &pTexData->height ); pTexData->view_width = pTexData->width; // undone: what is this? pTexData->view_height = pTexData->height; // undone: what is this? - + GetMaterialReflectivity( matID, pTexData->reflectivity.Base() ); g_SurfaceProperties[nOutput] = GetSurfaceProperties( matID, pName ); #if 0 - Msg( "reflectivity: %f %f %f\n", + Msg( "reflectivity: %f %f %f\n", pTexData->reflectivity[0], pTexData->reflectivity[1], pTexData->reflectivity[2] ); @@ -609,7 +616,7 @@ int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& o else if (bt->rotate == 270) { sinv = -1 ; cosv = 0; } else - { + { ang = bt->rotate / 180 * M_PI; sinv = sin(ang); cosv = cos(ang); @@ -621,14 +628,14 @@ int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& o sv = 1; else sv = 2; - + if (vecs[1][0]) tv = 0; else if (vecs[1][1]) tv = 1; else tv = 2; - + for (i=0 ; i<2 ; i++) { ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; @@ -659,7 +666,7 @@ int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& o tx.lightmapVecsLuxelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->lightmapWorldUnitsPerLuxel; tx.lightmapVecsLuxelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->lightmapWorldUnitsPerLuxel; tx.lightmapVecsLuxelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->lightmapWorldUnitsPerLuxel; - + tx.lightmapVecsLuxelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->lightmapWorldUnitsPerLuxel; tx.lightmapVecsLuxelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->lightmapWorldUnitsPerLuxel; tx.lightmapVecsLuxelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->lightmapWorldUnitsPerLuxel; @@ -668,16 +675,16 @@ int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& o shiftScaleV = bt->textureWorldUnitsPerTexel[1] / bt->lightmapWorldUnitsPerLuxel; } - tx.textureVecsTexelsPerWorldUnits[0][3] = bt->shift[0] + + tx.textureVecsTexelsPerWorldUnits[0][3] = bt->shift[0] + DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[0] ); - tx.textureVecsTexelsPerWorldUnits[1][3] = bt->shift[1] + + tx.textureVecsTexelsPerWorldUnits[1][3] = bt->shift[1] + DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[1] ); - + tx.lightmapVecsLuxelsPerWorldUnits[0][3] = shiftScaleU * bt->shift[0] + DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[0] ); tx.lightmapVecsLuxelsPerWorldUnits[1][3] = shiftScaleV * bt->shift[1] + DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[1] ); - + tx.flags = bt->flags; tx.texdata = FindOrCreateTexData( bt->name ); diff --git a/utils/vbsp/vbsp.cpp b/utils/vbsp/vbsp.cpp index 7b8c05936..29cfb1767 100644 --- a/utils/vbsp/vbsp.cpp +++ b/utils/vbsp/vbsp.cpp @@ -20,6 +20,11 @@ #include "byteswap.h" #include "worldvertextransitionfixup.h" +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#include "vscript_vbsp.h" +#endif + extern float g_maxLightmapDimension; char source[1024]; @@ -43,7 +48,11 @@ qboolean noshare; qboolean nosubdiv; qboolean notjunc; qboolean noopt; +#ifdef MAPBASE +qboolean noleaktest; +#else qboolean leaktest; +#endif // MAPBASE qboolean verboseentities; qboolean dumpcollide = false; qboolean g_bLowPriority = false; @@ -56,6 +65,17 @@ bool g_NodrawTriggers = false; bool g_DisableWaterLighting = false; bool g_bAllowDetailCracks = false; bool g_bNoVirtualMesh = false; +bool g_bNoHiddenManifestMaps = false; +#ifdef MAPBASE +bool g_bNoDefaultCubemaps = true; +bool g_bSkyboxCubemaps = false; +bool g_bPropperInsertAllAsStatic = false; +bool g_bPropperStripEntities = false; +int g_iDefaultCubemapSize = 32; +#endif // MAPBASE +#ifdef MAPBASE_VSCRIPT +ScriptLanguage_t g_iScripting = SL_NONE; +#endif // MAPBASE_VSCRIPT float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE; float g_luxelScale = 1.0f; @@ -68,7 +88,7 @@ char outbase[32]; char g_szEmbedDir[MAX_PATH] = { 0 }; -// HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper +// HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper // world coordinate extents. Assumes square spatial constraints. #define BLOCKS_SIZE 1024 #define BLOCKS_SPACE (COORD_EXTENT/BLOCKS_SIZE) @@ -182,14 +202,14 @@ void ProcessBlock_Thread (int threadnum, int blocknum) node->contents = CONTENTS_SOLID; block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = node; return; - } + } FixupAreaportalWaterBrushes( brushes ); if (!nocsg) brushes = ChopBrushes (brushes); tree = BrushBSP (brushes, mins, maxs); - + block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = tree->headnode; } @@ -296,7 +316,11 @@ void ProcessWorldModel (void) Warning( ("**** leaked ****\n") ); leaked = true; LeakFile (tree); +#ifdef MAPBASE + if (!noleaktest) +#else if (leaktest) +#endif // MAPBASE { Warning( ("--- MAP LEAKED ---\n") ); exit (0); @@ -343,7 +367,7 @@ void ProcessWorldModel (void) start = Plat_FloatTime(); Msg("FixTjuncs...\n"); - + // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions) // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList); @@ -397,7 +421,7 @@ void ProcessSubModel( ) if (!nocsg) list = ChopBrushes (list); tree = BrushBSP (list, mins, maxs); - + // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode. if ( tree->headnode->planenum == PLANENUM_LEAF ) { @@ -407,7 +431,7 @@ void ProcessSubModel( ) } MakeTreePortals (tree); - + #if DEBUG_BRUSHMODEL if ( entity_num == DEBUG_BRUSHMODEL ) WriteGLView( tree, "tree_all" ); @@ -418,7 +442,7 @@ void ProcessSubModel( ) FixTjuncs( tree->headnode, NULL ); WriteBSP( tree->headnode, NULL ); - + #if DEBUG_BRUSHMODEL if ( entity_num == DEBUG_BRUSHMODEL ) { @@ -604,7 +628,7 @@ static void EmitOccluderBrushes() int nIndex = g_OccluderInfo.AddToTail(); g_OccluderInfo[nIndex].m_nOccluderEntityIndex = entity_num; - + sideList.RemoveAll(); GenerateOccluderSideList( entity_num, sideList ); for ( int i = faceList.Count(); --i >= 0; ) @@ -642,7 +666,7 @@ static void EmitOccluderBrushes() { g_OccluderVertexIndices.AddToTail( f->vertexnums[k] ); - const Vector &p = dvertexes[f->vertexnums[k]].point; + const Vector &p = dvertexes[f->vertexnums[k]].point; VectorMin( occluderData.mins, p, occluderData.mins ); VectorMax( occluderData.maxs, p, occluderData.maxs ); } @@ -670,6 +694,8 @@ void SetOccluderArea( int nOccluder, int nArea, int nEntityNum ) { g_OccluderData[nOccluder].area = nArea; } + +#ifndef MAPBASE else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) ) { const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" ); @@ -679,6 +705,7 @@ void SetOccluderArea( int nOccluder, int nArea, int nEntityNum ) } Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName ); } +#endif // MAPBASE } @@ -712,7 +739,7 @@ void AssignAreaToOccluder( int nOccluder, tree_t *pTree, bool bCrossAreaPortals if ((!bCrossAreaPortals) && pPortal->nodes[nOtherSideIndex]->contents & CONTENTS_AREAPORTAL) continue; - int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0; + int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0; SetOccluderArea( nOccluder, nAdjacentArea, nEntityNum ); } } @@ -770,7 +797,7 @@ void MarkNoDynamicShadowSides() for ( int i=0; i < g_NoDynamicShadowSides.Count(); i++ ) { int brushSideID = g_NoDynamicShadowSides[i]; - + // Find the side with this ID. for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ ) { @@ -811,7 +838,7 @@ bool Is3DSkyboxArea( int area ) return false; } - + /* ============ ProcessModels @@ -827,7 +854,7 @@ void ProcessModels (void) // emit the displacement surfaces EmitInitialDispInfos(); - // Clip occluder brushes against each other, + // Clip occluder brushes against each other, // Remove them from the list of models to process below EmitOccluderBrushes( ); @@ -859,7 +886,12 @@ void ProcessModels (void) } // Turn the skybox into a cubemap in case we don't build env_cubemap textures. +#ifdef MAPBASE + if (!g_bNoDefaultCubemaps) + Cubemap_CreateDefaultCubemaps(); +#else Cubemap_CreateDefaultCubemaps(); +#endif // MAPBASE EndBSPFile (); } @@ -891,7 +923,7 @@ int RunVBSP( int argc, char **argv ) MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT, false, false, false, false ); InstallSpewFunction(); SpewActivate( "developer", 1 ); - + CmdLib_InitFileSystem( argv[ argc-1 ] ); Q_StripExtension( ExpandArg( argv[ argc-1 ] ), source, sizeof( source ) ); @@ -1000,11 +1032,19 @@ int RunVBSP( int argc, char **argv ) Msg ("microvolume = %f\n", microvolume); i++; } +#ifdef MAPBASE + else if (!Q_stricmp(argv[i], "-noleaktest")) + { + Msg ("noleaktest = true\n"); + noleaktest = true; + } +#else else if (!Q_stricmp(argv[i], "-leaktest")) { Msg ("leaktest = true\n"); leaktest = true; } +#endif // MAPBASE else if (!Q_stricmp(argv[i], "-verboseentities")) { Msg ("verboseentities = true\n"); @@ -1036,7 +1076,7 @@ int RunVBSP( int argc, char **argv ) block_yl = atoi(argv[i+2]); block_xh = atoi(argv[i+3]); block_yh = atoi(argv[i+4]); - Msg ("blocks: %i,%i to %i,%i\n", + Msg ("blocks: %i,%i to %i,%i\n", block_xl, block_yl, block_xh, block_yh); i+=4; } @@ -1137,6 +1177,108 @@ int RunVBSP( int argc, char **argv ) { EnableFullMinidumps( true ); } + else if ( !Q_stricmp( argv[i], "-nohiddenmaps" ) ) + { + g_bNoHiddenManifestMaps = true; + } +#ifdef MAPBASE + // Thanks to Mapbase's shader changes, default all-black cubemaps are no longer needed. + // The command has been switched from "-nodefaultcubemap" to "-defaultcubemap", + // meaning maps are compiled without them by default. + else if ( !Q_stricmp( argv[i], "-defaultcubemap" ) ) + { + g_bNoDefaultCubemaps = false; + } + // Default cubemaps are supposed to show the sky texture, but Valve disabled this + // because they didn't get it working for HDR cubemaps. As a result, all default + // cubemaps appear as all-black textures. However, this parameter has been added to + // re-enable skybox cubemaps for LDR cubemaps. (HDR skybox cubemaps are not supported) + else if ( !Q_stricmp( argv[i], "-skyboxcubemap" ) ) + { + g_bNoDefaultCubemaps = false; + g_bSkyboxCubemaps = true; + } + else if ( !Q_stricmp( argv[i], "-defaultcubemapres" ) ) + { + g_iDefaultCubemapSize = atoi( argv[i + 1] ); + Msg( "Default cubemap size = %i\n", g_iDefaultCubemapSize ); + i++; + } + else if ( !Q_stricmp( argv[i], "-defaultproppermodelsstatic" ) ) + { + g_bPropperInsertAllAsStatic = true; + } + else if ( !Q_stricmp( argv[i], "-strippropperentities" ) ) + { + g_bPropperStripEntities = true; + } +#endif // MAPBASE +#ifdef MAPBASE_VSCRIPT + else if ( !Q_stricmp( argv[i], "-scripting" ) ) + { + const char *pszScriptLanguage = argv[i + 1]; + if( pszScriptLanguage[0] == '-') + { + // It's another command. Just use default + g_iScripting = SL_DEFAULT; + } + else + { + // Use a specific language + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + g_iScripting = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + g_iScripting = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + g_iScripting = SL_PYTHON; + } + else if( !Q_stricmp(pszScriptLanguage, "lua") ) + { + g_iScripting = SL_LUA; + } + else + { + DevWarning("-server_script does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + g_iScripting = SL_NONE; + } + i++; + } + } + else if ( !Q_stricmp( argv[i], "-doc" ) ) + { + // Only print the documentation + + if (g_iScripting) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + VScriptVBSPInit(); + + const char *pszArg1 = argv[i + 1]; + if (pszArg1[0] == '-') + { + // It's another command. Just use * + pszArg1 = "*"; + } + + char szCommand[512]; + _snprintf( szCommand, sizeof( szCommand ), "__Documentation.PrintHelp( \"%s\" );", pszArg1 ); + g_pScriptVM->Run( szCommand ); + } + else + { + Warning("Cannot print documentation without scripting enabled!\n"); + } + + DeleteCmdLine( argc, argv ); + CmdLib_Cleanup(); + CmdLib_Exit( 1 ); + } +#endif // MAPBASE_VSCRIPT else if ( !Q_stricmp( argv[i], "-embed" ) && i < argc - 1 ) { V_MakeAbsolutePath( g_szEmbedDir, sizeof( g_szEmbedDir ), argv[++i], "." ); @@ -1164,7 +1306,7 @@ int RunVBSP( int argc, char **argv ) { PrintCommandLine( argc, argv ); - Warning( + Warning( "usage : vbsp [options...] mapfile\n" "example: vbsp -onlyents c:\\hl2\\hl2\\maps\\test\n" "\n" @@ -1303,7 +1445,7 @@ int RunVBSP( int argc, char **argv ) sprintf (path, "%s.lin", source); remove (path); - strcpy (name, ExpandArg (argv[i])); + strcpy (name, ExpandArg (argv[i])); const char *pszExtension = V_GetFileExtension( name ); if ( !pszExtension ) @@ -1414,7 +1556,7 @@ int RunVBSP( int argc, char **argv ) } end = Plat_FloatTime(); - + char str[512]; GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) ); Msg( "%s elapsed\n", str ); @@ -1423,6 +1565,9 @@ int RunVBSP( int argc, char **argv ) ReleasePakFileLumps(); DeleteMaterialReplacementKeys(); ShutdownMaterialSystem(); +#ifdef MAPBASE_VSCRIPT + VScriptVBSPTerm(); +#endif // MAPBASE_VSCRIPT CmdLib_Cleanup(); return 0; } diff --git a/utils/vbsp/vbsp.h b/utils/vbsp/vbsp.h index 689bbaa87..c6989bcb0 100644 --- a/utils/vbsp/vbsp.h +++ b/utils/vbsp/vbsp.h @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -9,7 +9,7 @@ #if !defined( VBSP_H ) #define VBSP_H - + #include "cmdlib.h" #include "mathlib/vector.h" #include "scriplib.h" @@ -19,6 +19,9 @@ #include "qfiles.h" #include "utilmatlib.h" #include "ChunkFile.h" +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#endif #ifdef WIN32 #pragma warning( disable: 4706 ) @@ -34,6 +37,12 @@ class CUtlBuffer; // this will output glview files for the given brushmodel. Brushmodel 1 is the world, 2 is the first brush entity, etc. #define DEBUG_BRUSHMODEL 0 +#ifdef MAPBASE +// Activates compiler code for parallax corrected cubemaps +// https://developer.valvesoftware.com/wiki/Parallax_Corrected_Cubemaps +#define PARALLAX_CORRECTED_CUBEMAPS 1 +#endif + struct portal_t; struct node_t; @@ -76,7 +85,7 @@ struct side_t qboolean bevel; // don't ever use for bsp splitting side_t *next; - int origIndex; + int origIndex; int id; // This is the unique id generated by worldcraft for this side. unsigned int smoothingGroups; CUtlVector aOverlayIds; // List of overlays that reside on this side. @@ -188,7 +197,7 @@ struct bspbrush_t #define MAX_NODE_BRUSHES 8 -struct leafface_t +struct leafface_t { face_t *pFace; leafface_t *pNext; @@ -334,6 +343,33 @@ class CMapFile int m_StartMapOverlays; int m_StartMapWaterOverlays; +#ifdef MAPBASE_VSCRIPT + HSCRIPT GetScriptInstance(); + + static ScriptHook_t g_Hook_OnMapLoaded; + + // VScript functions + ALLOW_SCRIPT_ACCESS(); +private: + + const Vector& GetMins() { return map_mins; } + const Vector& GetMaxs() { return map_maxs; } + + int GetNumMapBrushes() { return nummapbrushes; } + + const Vector& GetEntityOrigin(int idx) { return (idx < num_entities && idx >= 0) ? entities[idx].origin : vec3_origin; } + int GetEntityFirstBrush(int idx) { return (idx < num_entities && idx >= 0) ? entities[idx].firstbrush : 0; } + int GetEntityNumBrushes(int idx) { return (idx < num_entities && idx >= 0) ? entities[idx].numbrushes : 0; } + + void ScriptGetEntityKeyValues(int idx, HSCRIPT hKeyTable, HSCRIPT hValTable); + + int ScriptAddSimpleEntityKV(HSCRIPT hKV/*, const Vector& vecOrigin, int iFirstBrush, int iNumBrushes*/); + int ScriptAddInstance(const char *pszVMF, const Vector& vecOrigin, const QAngle& angAngles); + + int GetNumEntities() { return num_entities; } + + HSCRIPT m_hScriptInstance; +#endif }; extern CMapFile *g_MainMap; @@ -365,6 +401,11 @@ extern bool g_NodrawTriggers; extern bool g_DisableWaterLighting; extern bool g_bAllowDetailCracks; extern bool g_bNoVirtualMesh; +extern bool g_bNoHiddenManifestMaps; +#ifdef MAPBASE +extern bool g_bPropperInsertAllAsStatic; +extern bool g_bPropperStripEntities; +#endif // MAPBASE extern char outbase[32]; extern char source[1024]; @@ -455,7 +496,7 @@ enum detailscreen_e }; #define TRANSPARENT_CONTENTS (CONTENTS_GRATE|CONTENTS_WINDOW) - + #include "csg.h" //============================================================================= @@ -592,7 +633,7 @@ void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename ); void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *headnode ); //============================================================================= -// find + find or create the texdata +// find + find or create the texdata int FindTexData( const char *pName ); int FindOrCreateTexData( const char *pName ); // Add a clone of an existing texdata with a new name @@ -607,7 +648,12 @@ void SaveVertexNormals( void ); //============================================================================= // cubemap.cpp +#ifdef PARALLAX_CORRECTED_CUBEMAPS +extern char* g_pParallaxObbStrs[MAX_MAP_CUBEMAPSAMPLES]; +void Cubemap_InsertSample( const Vector& origin, int size, char* pParallaxObbStr ); +#else void Cubemap_InsertSample( const Vector& origin, int size ); +#endif // PARALLAX_CORRECTED_CUBEMAPS void Cubemap_CreateDefaultCubemaps( void ); void Cubemap_SaveBrushSides( const char *pSideListStr ); void Cubemap_FixupBrushSidesMaterials( void ); diff --git a/utils/vbsp/vbsp.vpc b/utils/vbsp/vbsp.vpc index 0d7b4db30..7ce84c13a 100644 --- a/utils/vbsp/vbsp.vpc +++ b/utils/vbsp/vbsp.vpc @@ -6,6 +6,7 @@ $Macro SRCDIR "..\.." $Macro OUTBINDIR "$LIBPUBLIC" +$Macro OUTBINNAME "vbsp" $Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc" @@ -15,6 +16,8 @@ $Configuration { $AdditionalIncludeDirectories "$BASE,..\common,..\vmpi" $PreprocessorDefinitions "$BASE;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE" + + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } $Linker @@ -65,6 +68,14 @@ $Project "Vbsp" $File "writebsp.cpp" $File "$SRCDIR\public\zip_utils.cpp" + $File "vscript_vbsp.cpp" [$MAPBASE_VSCRIPT] + $File "vscript_vbsp.h" [$MAPBASE_VSCRIPT] + $File "vscript_vbsp.nut" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vmfs.cpp" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vmfs.h" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vis.cpp" [$MAPBASE_VSCRIPT] + $File "vscript_funcs_vis.h" [$MAPBASE_VSCRIPT] + $Folder "Common Files" { $File "..\common\bsplib.cpp" @@ -176,6 +187,7 @@ $Project "Vbsp" $Lib tier2 $Lib vtf $Lib "$LIBCOMMON/lzma" + $Lib vscript [$MAPBASE_VSCRIPT] } $File "notes.txt" diff --git a/utils/vbsp/vscript_funcs_vis.cpp b/utils/vbsp/vscript_funcs_vis.cpp new file mode 100644 index 000000000..b0bff29be --- /dev/null +++ b/utils/vbsp/vscript_funcs_vis.cpp @@ -0,0 +1,29 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript functions for VIS +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" +#include "tier1/fmtstr.h" + +#include "vbsp.h" +#include "map.h" +#include "fgdlib/fgdlib.h" + +#include "vscript_vbsp.h" +#include "vscript_funcs_vis.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// There are currently no vis-related functions, but it would be nice to have them in the future. + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void RegisterVisScriptFunctions() +{ + //ScriptRegisterFunction( g_pScriptVM, VMFKV_CreateBlank, "Creates a CScriptKeyValues instance with VMF formatting." ); +} diff --git a/utils/vbsp/vscript_funcs_vis.h b/utils/vbsp/vscript_funcs_vis.h new file mode 100644 index 000000000..6eedbd68b --- /dev/null +++ b/utils/vbsp/vscript_funcs_vis.h @@ -0,0 +1,16 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: VScripts functions for VIS +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_VIS +#define VSCRIPT_FUNCS_VIS +#ifdef _WIN32 +#pragma once +#endif + +void RegisterVisScriptFunctions(); + +#endif diff --git a/utils/vbsp/vscript_funcs_vmfs.cpp b/utils/vbsp/vscript_funcs_vmfs.cpp new file mode 100644 index 000000000..785f74e4f --- /dev/null +++ b/utils/vbsp/vscript_funcs_vmfs.cpp @@ -0,0 +1,157 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" +#include "tier1/fmtstr.h" + +#include "vbsp.h" +#include "map.h" +#include "fgdlib/fgdlib.h" + +#include "vscript_vbsp.h" +#include "vscript_funcs_vmfs.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +static HSCRIPT VMFKV_CreateBlank() +{ + KeyValues *pKV = new KeyValues("VMF"); + + KeyValues *pWorld = pKV->FindKey( "world", true ); + if (pWorld) + { + pWorld->SetString( "classname", "worldspawn" ); + } + + return scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); +} + +static bool VMFKV_SaveToFile( const char *szFile, HSCRIPT hKV ) +{ + Warning( "Getting keyvalues from thing\n" ); + + KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hKV ); + if (!pKV) + return false; + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + for (KeyValues *pSubKey = pKV->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey()) + { + pSubKey->RecursiveSaveToFile( buf, 0 ); + } + + char pszFullName[MAX_PATH]; + Q_ExtractFilePath( source, pszFullName, sizeof(pszFullName) ); + V_snprintf( pszFullName, sizeof(pszFullName), "%s/vscript_io/%s", pszFullName, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + Warning( "Invalid file location : %s\n", szFile ); + buf.Purge(); + return false; + } + + int nSize = V_strlen(pszFullName) + 1; + char *pszDir = (char*)stackalloc(nSize); + V_memcpy( pszDir, pszFullName, nSize ); + V_StripFilename( pszDir ); + + //g_pFullFileSystem->RelativePathToFullPath( szFile, NULL, pszFullName, sizeof( pszFullName ) ); + Warning( "Full path is %s!\n", pszFullName ); + g_pFullFileSystem->CreateDirHierarchy( pszDir, NULL ); + bool res = g_pFullFileSystem->WriteFile( pszFullName, NULL, buf ); + buf.Purge(); + return res; +} + +static HSCRIPT VMFKV_LoadFromFile( const char *szFile ) +{ + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), NULL, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return NULL; + } + + KeyValues *pKV = new KeyValues( szFile ); + if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, NULL ) ) + { + pKV->deleteThis(); + return NULL; + } + + HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); // bAllowDestruct is supposed to automatically remove the involved KV + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +inline void ScriptVarsToKV( ScriptVariant_t &varKey, ScriptVariant_t &varValue, KeyValues *pKV ) +{ + switch (varValue.m_type) + { + case FIELD_CSTRING: pKV->SetString( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_INTEGER: pKV->SetInt( varKey.m_pszString, varValue.m_int ); break; + case FIELD_FLOAT: pKV->SetFloat( varKey.m_pszString, varValue.m_float ); break; + case FIELD_BOOLEAN: pKV->SetBool( varKey.m_pszString, varValue.m_bool ); break; + case FIELD_VECTOR: pKV->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break; + } +} + +static HSCRIPT VMFKV_AddEntityFromTables( HSCRIPT hVMF, HSCRIPT hKV, HSCRIPT hIO ) +{ + KeyValues *pVMF = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hVMF ); + if (!pVMF) + return false; + + KeyValues *pEnt = pVMF->CreateNewKey(); + if (!pEnt) + return false; + + pEnt->SetName( "entity" ); + + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( hKV, nIterator, &varKey, &varValue )) != -1) + { + ScriptVarsToKV( varKey, varValue, pEnt ); + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + + KeyValues *pConnections = pEnt->FindKey( "connections", true ); + if (hIO && pConnections) + { + nIterator = -1; + while ((nIterator = g_pScriptVM->GetKeyValue( hIO, nIterator, &varKey, &varValue )) != -1) + { + ScriptVarsToKV( varKey, varValue, pEnt ); + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } + } + + return scriptmanager->CreateScriptKeyValues( g_pScriptVM, pEnt, false ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void RegisterVMFScriptFunctions() +{ + ScriptRegisterFunction( g_pScriptVM, VMFKV_CreateBlank, "Creates a CScriptKeyValues instance with VMF formatting." ); + ScriptRegisterFunction( g_pScriptVM, VMFKV_SaveToFile, "Saves a CScriptKeyValues instance with VMF formatting." ); + ScriptRegisterFunction( g_pScriptVM, VMFKV_LoadFromFile, "Loads a VMF as a CScriptKeyValues instance with VMF formatting." ); + ScriptRegisterFunction( g_pScriptVM, VMFKV_AddEntityFromTables, "Adds a VMF-formatted entity to a CScriptKeyValues instance." ); +} diff --git a/utils/vbsp/vscript_funcs_vmfs.h b/utils/vbsp/vscript_funcs_vmfs.h new file mode 100644 index 000000000..9ca732e38 --- /dev/null +++ b/utils/vbsp/vscript_funcs_vmfs.h @@ -0,0 +1,16 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: VScript functions for VMFs +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_FUNCS_VMFS +#define VSCRIPT_FUNCS_VMFS +#ifdef _WIN32 +#pragma once +#endif + +void RegisterVMFScriptFunctions(); + +#endif diff --git a/utils/vbsp/vscript_vbsp.cpp b/utils/vbsp/vscript_vbsp.cpp new file mode 100644 index 000000000..00fed5108 --- /dev/null +++ b/utils/vbsp/vscript_vbsp.cpp @@ -0,0 +1,368 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase's implementation of VScript in VBSP, allowing users to modify map compilation behavior with scripts. +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" +#include "tier1/fmtstr.h" + +#include "vbsp.h" +#include "map.h" +#include "fgdlib/fgdlib.h" +#include "convar.h" + +#include "vscript_vbsp.h" +#include "vscript_vbsp.nut" +#include "vscript_funcs_vmfs.h" +#include "vscript_funcs_vis.h" + +IScriptVM *g_pScriptVM; +IScriptManager *scriptmanager = NULL; + +extern ScriptLanguage_t g_iScripting; + +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +// This is to ensure a dependency exists between the vscript library and the game DLLs +extern int vscript_token; +int vscript_token_hack = vscript_token; + +// HACKHACK: VScript library relies on developer convar existing +ConVar developer( "developer", "1", 0, "Set developer message level." ); // developer mode + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return NULL; + } + + static const char *pszExtensions[] = + { + "", // SL_NONE + ".gm", // SL_GAMEMONKEY + ".nut", // SL_SQUIRREL + ".lua", // SL_LUA + ".py", // SL_PYTHON + }; + + const char *pszVMExtension = pszExtensions[g_pScriptVM->GetLanguage()]; + const char *pszIncomingExtension = V_strrchr( pszScriptName , '.' ); + if ( pszIncomingExtension && V_strcmp( pszIncomingExtension, pszVMExtension ) != 0 ) + { + Warning( "Script file type does not match VM type\n" ); + return NULL; + } + + CFmtStr scriptPath; + if ( pszIncomingExtension ) + { + scriptPath = pszScriptName; + } + else + { + scriptPath.sprintf( "%s%s", pszScriptName, pszVMExtension ); + } + + const char *pBase; + CUtlBuffer bufferScript; + + if ( g_pScriptVM->GetLanguage() == SL_PYTHON ) + { + // python auto-loads raw or precompiled modules - don't load data here + pBase = NULL; + } + else + { + /* + FileFindHandle_t handle = NULL; + const char *file = g_pFullFileSystem->FindFirst( "*", &handle ); + while (file) + { + Msg( "File in this directory: %s\n", file ); + file = g_pFullFileSystem->FindNext(handle); + } + Msg( "File exists: %d\n", g_pFullFileSystem->FileExists( scriptPath ) ); + */ + + + bool bResult = g_pFullFileSystem->ReadFile( scriptPath, NULL, bufferScript ); + + if ( !bResult && bWarnMissing ) + { + Warning( "Script not found (%s) \n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + + pBase = (const char *) bufferScript.Base(); + + if ( !pBase || !*pBase ) + { + return NULL; + } + } + + + const char *pszFilename = V_strrchr( scriptPath, '\\' ); + pszFilename++; + HSCRIPT hScript = g_pScriptVM->CompileScript( pBase, pszFilename ); + if ( !hScript ) + { + Warning( "FAILED to compile and execute script file named %s\n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + return hScript; +} + +static int g_ScriptVBSPRunScriptDepth; + +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + if ( !pszScriptName || !*pszScriptName ) + { + Warning( "Cannot run script: NULL script name\n" ); + return false; + } + + // Prevent infinite recursion in VM + if ( g_ScriptVBSPRunScriptDepth > 16 ) + { + Warning( "IncludeScript stack overflow\n" ); + return false; + } + + g_ScriptVBSPRunScriptDepth++; + HSCRIPT hScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + bool bSuccess = false; + if ( hScript ) + { + bSuccess = ( g_pScriptVM->Run( hScript, hScope ) != SCRIPT_ERROR ); + if ( !bSuccess ) + { + Warning( "Error running script named %s\n", pszScriptName ); + Assert( "Error running script" ); + } + } + g_ScriptVBSPRunScriptDepth--; + return bSuccess; +} + +ScriptHook_t CMapFile::g_Hook_OnMapLoaded; + +BEGIN_SCRIPTDESC_ROOT( CMapFile, "Map file" ) + + DEFINE_SCRIPTFUNC( GetMins, "Get the map's mins." ) + DEFINE_SCRIPTFUNC( GetMaxs, "Get the map's maxs." ) + + DEFINE_SCRIPTFUNC( GetEntityOrigin, "Get the origin of the entity with the specified index." ) + DEFINE_SCRIPTFUNC( GetEntityFirstBrush, "Get the first brush ID of the entity with the specified index." ) + DEFINE_SCRIPTFUNC( GetEntityNumBrushes, "Get the number of brushes in the entity with the specified index." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetEntityKeyValues, "GetEntityKeyValues", "Export an entity's keyvalues to two arrays." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddSimpleEntityKV, "AddSimpleEntityKV", "Add a simple entity from a keyvalue table." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddInstance, "AddInstance", "Add an instance to the map." ) + + DEFINE_SCRIPTFUNC( GetNumEntities, "Get the number of entities in the map." ) + + // + // Hooks + // + DEFINE_SIMPLE_SCRIPTHOOK( CMapFile::g_Hook_OnMapLoaded, "OnMapLoaded", FIELD_VOID, "Called when the NPC is deciding whether to hear a CSound or not." ) + +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; +} + +static const char *GetMapBase() +{ + return mapbase; +} + +static HSCRIPT GetMainMap() +{ + return g_MainMap ? g_MainMap->GetScriptInstance() : NULL; +} + +static HSCRIPT GetLoadingMap() +{ + return g_LoadingMap ? g_LoadingMap->GetScriptInstance() : NULL; +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +extern qboolean glview; +extern qboolean onlyents; +extern bool onlyprops; +extern qboolean noleaktest; +extern qboolean verboseentities; +extern qboolean g_bLowPriority; +extern bool g_bKeepStaleZip; +extern bool g_bNoDefaultCubemaps; +extern bool g_bSkyboxCubemaps; +extern int g_iDefaultCubemapSize; + +bool VScriptVBSPInit() +{ + VMPROF_START + + if( g_iScripting != SL_NONE && scriptmanager != NULL ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( g_iScripting ); + + if( g_pScriptVM ) + { + 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." ); + ScriptRegisterFunction( g_pScriptVM, GetLoadingMap, "Gets the map which is currently loading (e.g. an instance)." ); + + ScriptRegisterFunction( g_pScriptVM, DoUniqueString, SCRIPT_ALIAS( "UniqueString", "Generate a string guaranteed to be unique across the life of the script VM, with an optional root string. Useful for adding data to tables when not sure what keys are already in use in that table." ) ); + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + + ScriptRegisterConstantNamed( g_pScriptVM, GameData::NAME_FIXUP_PREFIX, "NAME_FIXUP_PREFIX", "Prefix name fixup" ); + ScriptRegisterConstantNamed( g_pScriptVM, GameData::NAME_FIXUP_POSTFIX, "NAME_FIXUP_PREFIX", "Postfix name fixup" ); + ScriptRegisterConstantNamed( g_pScriptVM, GameData::NAME_FIXUP_NONE, "NAME_FIXUP_NONE", "No name fixup" ); + + ScriptRegisterConstant( g_pScriptVM, microvolume, "" ); + ScriptRegisterConstant( g_pScriptVM, noprune, "" ); + ScriptRegisterConstant( g_pScriptVM, glview, "" ); + ScriptRegisterConstant( g_pScriptVM, nodetail, "" ); + ScriptRegisterConstant( g_pScriptVM, fulldetail, "" ); + ScriptRegisterConstant( g_pScriptVM, onlyents, "" ); + ScriptRegisterConstant( g_pScriptVM, onlyprops, "" ); + ScriptRegisterConstant( g_pScriptVM, nomerge, "" ); + ScriptRegisterConstant( g_pScriptVM, nomergewater, "" ); + ScriptRegisterConstant( g_pScriptVM, nowater, "" ); + ScriptRegisterConstant( g_pScriptVM, nocsg, "" ); + ScriptRegisterConstant( g_pScriptVM, noweld, "" ); + ScriptRegisterConstant( g_pScriptVM, noshare, "" ); + ScriptRegisterConstant( g_pScriptVM, nosubdiv, "" ); + ScriptRegisterConstant( g_pScriptVM, notjunc, "" ); + ScriptRegisterConstant( g_pScriptVM, noopt, "" ); + ScriptRegisterConstant( g_pScriptVM, noleaktest, "" ); + ScriptRegisterConstant( g_pScriptVM, verboseentities, "" ); + ScriptRegisterConstant( g_pScriptVM, dumpcollide, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bLowPriority, "" ); + ScriptRegisterConstant( g_pScriptVM, g_DumpStaticProps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bSkyVis, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bLightIfMissing, "" ); + ScriptRegisterConstant( g_pScriptVM, g_snapAxialPlanes, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bKeepStaleZip, "" ); + ScriptRegisterConstant( g_pScriptVM, g_NodrawTriggers, "" ); + ScriptRegisterConstant( g_pScriptVM, g_DisableWaterLighting, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bAllowDetailCracks, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bNoVirtualMesh, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bNoHiddenManifestMaps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bNoDefaultCubemaps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_bSkyboxCubemaps, "" ); + ScriptRegisterConstant( g_pScriptVM, g_iDefaultCubemapSize, "" ); + + if (g_iScripting == SL_SQUIRREL) + { + g_pScriptVM->Run( g_Script_vscript_vbsp ); + } + + RegisterVMFScriptFunctions(); + + // Run the map's script + char script[96]; + Q_snprintf( script, sizeof(script), "%s_vbsp", source ); + //Msg("VBSP script: \"%s\"\n", script); + VScriptRunScript( script, true ); + + VMPROF_SHOW( g_iScripting, "virtual machine startup" ); + + return true; + } + else + { + DevWarning("VM Did not start!\n"); + } + } + else + { + Log( "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptVBSPTerm() +{ + if( g_pScriptVM != NULL ) + { + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} diff --git a/utils/vbsp/vscript_vbsp.h b/utils/vbsp/vscript_vbsp.h new file mode 100644 index 000000000..298e3c3a4 --- /dev/null +++ b/utils/vbsp/vscript_vbsp.h @@ -0,0 +1,27 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase's implementation of VScript in VBSP, allowing users to modify map compilation behavior with scripts. +// +// $NoKeywords: $ +//==================================================================================// + +#ifndef VSCRIPT_VBSP_H +#define VSCRIPT_VBSP_H + +#include "vscript/ivscript.h" + +#ifdef _WIN32 +#pragma once +#endif + +extern IScriptVM *g_pScriptVM; +extern IScriptManager *scriptmanager; + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing = false ); +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = false ) { return VScriptRunScript( pszScriptName, NULL, bWarnMissing ); } + +bool VScriptVBSPInit(); +void VScriptVBSPTerm(); + +#endif // VSCRIPT_SERVER_H diff --git a/utils/vbsp/vscript_vbsp.nut b/utils/vbsp/vscript_vbsp.nut new file mode 100644 index 000000000..8d6c8be35 --- /dev/null +++ b/utils/vbsp/vscript_vbsp.nut @@ -0,0 +1,48 @@ +static char g_Script_vscript_vbsp[] = R"vscript( +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Integrate VBSP into VScript Squirrel +// +//==================================================================================// +function UniqueString( string = "" ) +{ + return ::DoUniqueString( string.tostring() ); +} +function __ReplaceClosures( script, scope ) +{ + if ( !scope ) + { + scope = getroottable(); + } + local tempParent = { getroottable = function() { return null; } }; + local temp = { runscript = script }; + temp.set_delegate(tempParent); + temp.runscript() + foreach( key,val in temp ) + { + if ( typeof(val) == "function" && key != "runscript" ) + { + printl( " Replacing " + key ); + scope[key] <- val; + } + } +} + +function IncludeScript( name, scope = null ) +{ + if ( !scope ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +// VBSP logs don't support ConColorMsg() +print <- Msg + +function printdoc( text ) +{ + return ::print(text + "\n"); +} + +)vscript"; diff --git a/utils/vrad/vrad_dll.vpc b/utils/vrad/vrad_dll.vpc index c9b66d490..18de657b2 100644 --- a/utils/vrad/vrad_dll.vpc +++ b/utils/vrad/vrad_dll.vpc @@ -6,6 +6,7 @@ $Macro SRCDIR "..\.." $Macro OUTBINDIR "$LIBPUBLIC" +$Macro OUTBINNAME "vrad_dll" $Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" diff --git a/utils/vrad_launcher/vrad_launcher.cpp b/utils/vrad_launcher/vrad_launcher.cpp index a4d318342..689917d7d 100644 --- a/utils/vrad_launcher/vrad_launcher.cpp +++ b/utils/vrad_launcher/vrad_launcher.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -17,18 +17,18 @@ char* GetLastErrorString() { static char err[2048]; - + LPVOID lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, - NULL + NULL ); strncpy( err, (char*)lpMsgBuf, sizeof( err ) ); @@ -92,31 +92,43 @@ int main(int argc, char* argv[]) else printf( "Can't find '%s' specified in vrad.redirect.\n", dllName ); } - + fclose( fp ); } int returnValue = 0; - + for(int mode=0;mode<2;mode++) { if (mode && (! both_arg)) continue; - - // If it didn't load the module above, then use the +#ifdef MAPBASE + // Coming through! + if ( !pModule ) + { + // With this, we just load the DLL with our filename. + // This allows for custom DLLs without having to bother with the launcher. + char filename[64]; + Q_FileBase(argv[0], filename, sizeof(filename)); + Q_snprintf(dllName, sizeof(dllName), "%s%s", filename, "_dll.dll"); + pModule = Sys_LoadModule( dllName ); + } +#endif // MAPBASE + + // If it didn't load the module above, then use the if ( !pModule ) { strcpy( dllName, "vrad_dll.dll" ); pModule = Sys_LoadModule( dllName ); } - + if( !pModule ) { printf( "vrad_launcher error: can't load %s\n%s", dllName, GetLastErrorString() ); return 1; } - + CreateInterfaceFn fn = Sys_GetFactory( pModule ); if( !fn ) { @@ -124,7 +136,7 @@ int main(int argc, char* argv[]) Sys_UnloadModule( pModule ); return 2; } - + int retCode = 0; IVRadDLL *pDLL = (IVRadDLL*)fn( VRAD_INTERFACE_VERSION, &retCode ); if( !pDLL ) @@ -133,7 +145,7 @@ int main(int argc, char* argv[]) Sys_UnloadModule( pModule ); return 3; } - + if (both_arg) strcpy(argv[both_arg],(mode)?"-hdr":"-ldr"); returnValue = pDLL->main( argc, argv ); diff --git a/utils/vvis/vvis_dll.vpc b/utils/vvis/vvis_dll.vpc index b1715a2c6..d2ad27817 100644 --- a/utils/vvis/vvis_dll.vpc +++ b/utils/vvis/vvis_dll.vpc @@ -6,6 +6,7 @@ $Macro SRCDIR "..\.." $Macro OUTBINDIR "$LIBPUBLIC" +$Macro OUTBINNAME "vvis_dll" $Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" diff --git a/utils/vvis_launcher/vvis_launcher.cpp b/utils/vvis_launcher/vvis_launcher.cpp index edf03d251..2aa926fe8 100644 --- a/utils/vvis_launcher/vvis_launcher.cpp +++ b/utils/vvis_launcher/vvis_launcher.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ // @@ -15,22 +15,21 @@ #include "ilaunchabledll.h" - char* GetLastErrorString() { static char err[2048]; - + LPVOID lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, - NULL + NULL ); strncpy( err, (char*)lpMsgBuf, sizeof( err ) ); @@ -45,14 +44,40 @@ char* GetLastErrorString() int main(int argc, char* argv[]) { CommandLine()->CreateCmdLine( argc, argv ); +#ifndef MAPBASE + const char *pDLLName = "vvis_dll.dll"; + CSysModule *pModule = Sys_LoadModule( pDLLName ); + + if ( !pModule ) + { + printf( "vvis launcher error: can't load %s\n%s", pDLLName, GetLastErrorString() ); + return 1; + } +#else + // Coming through! const char *pDLLName = "vvis_dll.dll"; - + + // With this, we just load the DLL with our filename. + // This allows for custom DLLs without having to bother with the launcher. + char filename[128]; + Q_FileBase(argv[0], filename, sizeof(filename)); + Q_snprintf(filename, sizeof(filename), "%s_dll.dll", filename); + pDLLName = filename; + CSysModule *pModule = Sys_LoadModule( pDLLName ); + if ( !pModule ) + { + // Try loading the default then + pDLLName = "vvis_dll.dll"; + pModule = Sys_LoadModule( pDLLName ); + } + if ( !pModule ) { printf( "vvis launcher error: can't load %s\n%s", pDLLName, GetLastErrorString() ); return 1; } +#endif // MAPBASE CreateInterfaceFn fn = Sys_GetFactory( pModule ); if( !fn ) diff --git a/utils/vvis_launcher/vvis_launcher.vpc b/utils/vvis_launcher/vvis_launcher.vpc index a3c5ba554..5abf62938 100644 --- a/utils/vvis_launcher/vvis_launcher.vpc +++ b/utils/vvis_launcher/vvis_launcher.vpc @@ -30,7 +30,7 @@ $Project "Vvis_launcher" $Folder "Source Files" { $File "vvis_launcher.cpp" - + $File "StdAfx.cpp" { $Configuration diff --git a/vgui2/vgui_controls/AnimationController.cpp b/vgui2/vgui_controls/AnimationController.cpp index 9f92a74a6..6d89c172b 100644 --- a/vgui2/vgui_controls/AnimationController.cpp +++ b/vgui2/vgui_controls/AnimationController.cpp @@ -1,10 +1,10 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data - +{ + "workbench.iconTheme": "vscode-icons", + "git.autofetch": true, + "workbench.colorTheme": "Visual Studio Dark - C++", + "explorer.confirmDelete": false +} "window.zoomLevel": 1 +} #include #include #include @@ -31,6 +31,11 @@ using namespace vgui; static CUtlSymbolTable g_ScriptSymbols(0, 128, true); +#ifdef MAPBASE +// Allows animation sequences to be overridden by map-specific files +extern bool g_bUsingCustomHudAnimations; +#endif + // singleton accessor for animation controller for use by the vgui controls namespace vgui { @@ -59,8 +64,8 @@ AnimationController::AnimationController(Panel *parent) : BaseClass(parent, NULL // get the names of common types m_sPosition = g_ScriptSymbols.AddString("position"); - m_sSize = g_ScriptSymbols.AddString("size"); - m_sFgColor = g_ScriptSymbols.AddString("fgcolor"); + m_sSize = g_ScriptSymbols.AddString("size"); + m_sFgColor = g_ScriptSymbols.AddString("fgcolor"); m_sBgColor = g_ScriptSymbols.AddString("bgcolor"); m_sXPos = g_ScriptSymbols.AddString("xpos"); @@ -116,7 +121,7 @@ void AnimationController::ReloadScriptFile() { // Clear all current sequences m_Sequences.RemoveAll(); - + UpdateScreenSize(); // Reload each file we've loaded @@ -188,7 +193,7 @@ AnimationController::RelativeAlignmentLookup AnimationController::g_AlignmentLoo }; //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- AnimationController::RelativeAlignment AnimationController::LookupAlignment( char const *token ) { @@ -206,7 +211,7 @@ AnimationController::RelativeAlignment AnimationController::LookupAlignment( cha } //----------------------------------------------------------------------------- -// Purpose: Parse position including right edge and center adjustment out of a +// Purpose: Parse position including right edge and center adjustment out of a // token. This is relative to the screen //----------------------------------------------------------------------------- void AnimationController::SetupPosition( AnimCmdAnimate_t& cmd, float *output, char const *psz, int screendimension ) @@ -239,7 +244,7 @@ void AnimationController::SetupPosition( AnimCmdAnimate_t& cmd, float *output, c if ( Q_strlen( panelName ) > 0 ) { - // + // cmd.align.relativePosition = true; cmd.align.alignPanel = g_ScriptSymbols.AddString(panelName); cmd.align.alignment = ra; @@ -318,18 +323,44 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) Warning("Couldn't parse script file: expected , found nothing\n"); return false; } - - int seqIndex; + + int seqIndex = -1; UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token); - + // Create a new sequence - seqIndex = m_Sequences.AddToTail(); +#ifdef MAPBASE + if (g_bUsingCustomHudAnimations) + { + // look through for the sequence + for (seqIndex = 0; seqIndex < m_Sequences.Count(); seqIndex++) + { + if (m_Sequences[seqIndex].name == nameIndex) + break; + } + + if (seqIndex >= m_Sequences.Count()) + seqIndex = -1; + else + { + // Clear some stuff + m_Sequences[seqIndex].cmdList.RemoveAll(); + } + } + + if (seqIndex == -1) +#endif // MAPBASE + { + // Create a new sequence + seqIndex = m_Sequences.AddToTail(); + } + AnimSequence_t &seq = m_Sequences[seqIndex]; seq.name = nameIndex; seq.duration = 0.0f; // get the open brace or a conditional pMem = ParseFile(pMem, token, NULL); + if ( Q_stristr( token, "[$" ) || Q_stristr( token, "[!$" ) ) { bAccepted = EvaluateConditional( token ); @@ -395,16 +426,16 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) // XPos and YPos both use target ".a" SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenTall ); } - else + else { // parse the floating point values right out if (0 == sscanf(token, "%f %f %f %f", &cmdAnimate.target.a, &cmdAnimate.target.b, &cmdAnimate.target.c, &cmdAnimate.target.d)) { //============================================================================= // HPE_BEGIN: - // [pfreese] Improved handling colors not defined in scheme + // [pfreese] Improved handling colors not defined in scheme //============================================================================= - + // could be referencing a value in the scheme file, lookup Color default_invisible_black(0, 0, 0, 0); Color col = scheme->GetColor(token, default_invisible_black); @@ -423,7 +454,7 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) // Warning("Missing color in scheme: %s\n", token); // } } - + //============================================================================= // HPE_END //============================================================================= @@ -453,7 +484,7 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) cmdAnimate.target.a = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) ); } } - + // interpolation function pMem = ParseFile(pMem, token, NULL); if (!stricmp(token, "Accel")) @@ -652,7 +683,7 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) Warning("Couldn't parse script sequence '%s': expected , found '%s'\n", g_ScriptSymbols.String(seq.name), token); return false; } - + // Look ahead one token for a conditional char *peek = ParseFile(pMem, token, NULL); if ( Q_stristr( token, "[$" ) || Q_stristr( token, "[!$" ) ) @@ -669,7 +700,7 @@ bool AnimationController::ParseScriptFile(char *pMem, int length) { // Attempt to find a collision in the sequences, replacing the old one if found int seqIterator; - for ( seqIterator = 0; seqIterator < m_Sequences.Count()-1; seqIterator++ ) + for ( seqIterator = 0; seqIterator < m_Sequences.Count()-1; seqIterator++ ) { if ( m_Sequences[seqIterator].name == nameIndex ) { @@ -732,14 +763,14 @@ void AnimationController::UpdatePostedMessages(bool bRunToCompletion) curEvent.event = msg.event; curEvent.pParent = msg.parent.Get(); - + // run the event, but only if we haven't already run it this frame, for this parent if (!eventsRanThisFrame.HasElement(curEvent)) { eventsRanThisFrame.AddToTail(curEvent); RunCmd_RunEvent(msg); } - } + } break; case CMD_RUNEVENTCHILD: { @@ -749,7 +780,7 @@ void AnimationController::UpdatePostedMessages(bool bRunToCompletion) curEvent.pParent = msg.parent.Get()->FindChildByName( g_ScriptSymbols.String(msg.variable), true ); msg.parent = curEvent.pParent; - + // run the event, but only if we haven't already run it this frame, for this parent if (!eventsRanThisFrame.HasElement(curEvent)) { @@ -885,9 +916,9 @@ bool AnimationController::UpdateScreenSize() surface()->GetScreenSize(screenWide, screenTall); } - bool changed = m_nScreenBounds[ 0 ] != sx || + bool changed = m_nScreenBounds[ 0 ] != sx || m_nScreenBounds[ 1 ] != sy || - m_nScreenBounds[ 2 ] != screenWide || + m_nScreenBounds[ 2 ] != screenWide || m_nScreenBounds[ 3 ] != screenTall; m_nScreenBounds[ 0 ] = sx; @@ -1033,7 +1064,7 @@ void AnimationController::SetAutoReloadScript(bool state) //----------------------------------------------------------------------------- bool AnimationController::StartAnimationSequence(const char *sequenceName, bool bCanBeCancelled ) { - // We support calling an animation on elements that are not the calling + // We support calling an animation on elements that are not the calling // panel's children. Use the base parent to start the search. return StartAnimationSequence( GetParent(), sequenceName, bCanBeCancelled ); @@ -1078,7 +1109,7 @@ bool AnimationController::StartAnimationSequence(Panel *pWithinParent, const cha ExecAnimationCommand(seqName, m_Sequences[i].cmdList[cmdIndex], pWithinParent, bCanBeCancelled); } - return true; + return true; } //----------------------------------------------------------------------------- @@ -1374,7 +1405,7 @@ void AnimationController::RunCmd_StopPanelAnimations(PostedMessage_t &msg) if (!panel) return; - // loop through all the active animations cancelling any that + // loop through all the active animations cancelling any that // are operating on said panel, except for the event specified for (int i = 0; i < m_ActiveAnimations.Count(); i++) { @@ -1400,7 +1431,7 @@ void AnimationController::RunCmd_StopAnimation(PostedMessage_t &msg) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void AnimationController::RunCmd_SetFont( PostedMessage_t &msg ) { @@ -1426,7 +1457,7 @@ void AnimationController::RunCmd_SetFont( PostedMessage_t &msg ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void AnimationController::RunCmd_SetTexture( PostedMessage_t &msg ) { @@ -1445,7 +1476,7 @@ void AnimationController::RunCmd_SetTexture( PostedMessage_t &msg ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void AnimationController::RunCmd_SetString( PostedMessage_t &msg ) { @@ -1464,7 +1495,7 @@ void AnimationController::RunCmd_SetString( PostedMessage_t &msg ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- int AnimationController::GetRelativeOffset( AnimAlign_t& align, bool xcoord ) { @@ -1748,7 +1779,7 @@ PanelAnimationMap *CPanelAnimationDictionary::FindPanelAnimationMap( char const } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- PanelAnimationMap *CPanelAnimationDictionary::FindOrAddPanelAnimationMap( char const *className ) { @@ -1765,7 +1796,7 @@ PanelAnimationMap *CPanelAnimationDictionary::FindOrAddPanelAnimationMap( char c } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void CPanelAnimationDictionary::PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive ) { @@ -1787,7 +1818,7 @@ void CPanelAnimationDictionary::PanelAnimationDumpMap( PanelAnimationMap *map, b } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void CPanelAnimationDictionary::PanelAnimationDumpVars( char const *className ) { @@ -1822,7 +1853,7 @@ CPanelAnimationDictionary& GetPanelAnimationDictionary() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className ) { @@ -1838,7 +1869,7 @@ PanelAnimationMap *FindPanelAnimationMap( char const *className ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void PanelAnimationDumpVars( char const *className ) { diff --git a/vgui2/vgui_controls/Panel.cpp b/vgui2/vgui_controls/Panel.cpp index ec2c56534..21ca53601 100644 --- a/vgui2/vgui_controls/Panel.cpp +++ b/vgui2/vgui_controls/Panel.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ //=============================================================================// @@ -79,6 +79,13 @@ static char *CopyString( const char *in ) return n; } +#ifdef MAPBASE +ConVar vgui_mapbase_custom_schemes( "vgui_mapbase_custom_schemes", "1" ); + +// This is used in mapbase_shared.cpp +HScheme g_iCustomClientSchemeOverride; +#endif // MAPBASE + #ifdef STAGING_ONLY ConVar tf_strict_mouse_up_events( "tf_strict_mouse_up_events", "0", FCVAR_ARCHIVE, "Only allow Mouse-Release events to happens on panels we also Mouse-Downed in" ); #endif @@ -88,7 +95,7 @@ ConVar tf_debug_tabcontainer( "tf_debug_tabcontainer", "0", FCVAR_HIDDEN, "Spew #if defined( VGUI_USEDRAGDROP ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- struct vgui::DragDrop_t { @@ -312,7 +319,7 @@ class CKeyBindingsMgr kb->m_Panels.FindAndRemove( panel ); } } - + KBContext_t *Find( KeyBindingContextHandle_t handle ) { KBContext_t search; @@ -360,11 +367,11 @@ class CKeyBindingsMgr //----------------------------------------------------------------------------- // Purpose: static method - // Input : index - + // Input : index - // Output : Panel //----------------------------------------------------------------------------- Panel *GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ) - { + { KBContext_t *kb = Find( handle ); if ( kb ) { @@ -383,7 +390,7 @@ static CKeyBindingsMgr g_KBMgr; //----------------------------------------------------------------------------- // Purpose: Static method to allocate a context -// Input : - +// Input : - // Output : KeyBindingContextHandle_t //----------------------------------------------------------------------------- KeyBindingContextHandle_t Panel::CreateKeyBindingsContext( char const *filename, char const *pathID /*=0*/ ) @@ -396,7 +403,7 @@ Panel* Panel::m_sMousePressedPanels[] = { NULL, NULL, NULL }; //----------------------------------------------------------------------------- // Purpose: static method -// Input : - +// Input : - // Output : int //----------------------------------------------------------------------------- int Panel::GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ) @@ -406,7 +413,7 @@ int Panel::GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ) //----------------------------------------------------------------------------- // Purpose: static method -// Input : index - +// Input : index - // Output : Panel //----------------------------------------------------------------------------- Panel *Panel::GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ) @@ -434,7 +441,7 @@ int Panel::GetKeyMappingCount( ) //----------------------------------------------------------------------------- // Purpose: static method. Reverts key bindings for all registered panels (panels with keybindings actually // loaded from file -// Input : - +// Input : - //----------------------------------------------------------------------------- void Panel::RevertKeyBindings( KeyBindingContextHandle_t handle ) { @@ -464,8 +471,8 @@ static void BufPrint( CUtlBuffer& buf, int level, char const *fmt, ... ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : handle - +// Purpose: +// Input : handle - //----------------------------------------------------------------------------- void Panel::SaveKeyBindings( KeyBindingContextHandle_t handle ) { @@ -477,7 +484,7 @@ void Panel::SaveKeyBindings( KeyBindingContextHandle_t handle ) //----------------------------------------------------------------------------- // Purpose: static method. Saves key binding files out for all keybindings -// Input : - +// Input : - //----------------------------------------------------------------------------- void Panel::SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const *filename, char const *pathID /*= 0*/ ) { @@ -499,7 +506,7 @@ void Panel::SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const if ( !kbPanel->GetName() || !kbPanel->GetName()[ 0 ] ) continue; - + BufPrint( buf, 1, "\"%s\"\n", kbPanel->GetName() ); BufPrint( buf, 1, "{\n" ); @@ -525,9 +532,9 @@ void Panel::SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const } //----------------------------------------------------------------------------- -// Purpose: -// Input : handle - -// *panelOfInterest - +// Purpose: +// Input : handle - +// *panelOfInterest - //----------------------------------------------------------------------------- void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel *panelOfInterest ) { @@ -549,7 +556,7 @@ void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel { Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); Assert( kbPanel ); - + char const *panelName = kbPanel->GetName(); if ( !panelName ) { @@ -565,7 +572,7 @@ void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName ); continue; } - + kbPanel->ParseKeyBindings( subKey ); } } @@ -574,7 +581,7 @@ void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel //----------------------------------------------------------------------------- // Purpose: static method. Loads all key bindings again -// Input : - +// Input : - //----------------------------------------------------------------------------- void Panel::ReloadKeyBindings( KeyBindingContextHandle_t handle ) @@ -590,7 +597,7 @@ void Panel::ReloadKeyBindings( KeyBindingContextHandle_t handle ) { Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); Assert( kbPanel ); - + char const *panelName = kbPanel->GetName(); if ( !panelName ) { @@ -603,7 +610,7 @@ void Panel::ReloadKeyBindings( KeyBindingContextHandle_t handle ) Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName ); continue; } - + kbPanel->ParseKeyBindings( subKey ); } } @@ -826,15 +833,15 @@ void Panel::MakeReadyForUse() surface()->SolveTraverse( GetVPanel(), true ); } - + //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetName( const char *panelName ) { // No change? - if ( _panelName && - panelName && + if ( _panelName && + panelName && !Q_strcmp( _panelName, panelName ) ) { return; @@ -889,7 +896,7 @@ const char *Panel::GetClassName() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetPos(int x, int y) { @@ -901,7 +908,7 @@ void Panel::SetPos(int x, int y) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::GetPos(int &x, int &y) { @@ -909,7 +916,7 @@ void Panel::GetPos(int &x, int &y) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- int Panel::GetXPos() { @@ -919,7 +926,7 @@ int Panel::GetXPos() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- int Panel::GetYPos() { @@ -929,7 +936,7 @@ int Panel::GetYPos() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetSize(int wide, int tall) { @@ -939,7 +946,7 @@ void Panel::SetSize(int wide, int tall) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::GetSize(int &wide, int &tall) { @@ -947,7 +954,7 @@ void Panel::GetSize(int &wide, int &tall) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetBounds(int x, int y, int wide, int tall) { @@ -956,7 +963,7 @@ void Panel::SetBounds(int x, int y, int wide, int tall) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::GetBounds(int &x, int &y, int &wide, int &tall) { @@ -1031,7 +1038,7 @@ void Panel::OnScreenSizeChanged(int nOldWide, int nOldTall) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetVisible(bool state) { @@ -1039,7 +1046,7 @@ void Panel::SetVisible(bool state) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool Panel::IsVisible() { @@ -1052,7 +1059,7 @@ bool Panel::IsVisible() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetEnabled(bool state) { @@ -1065,7 +1072,7 @@ void Panel::SetEnabled(bool state) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool Panel::IsEnabled() { @@ -1073,7 +1080,7 @@ bool Panel::IsEnabled() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool Panel::IsPopup() { @@ -1081,7 +1088,7 @@ bool Panel::IsPopup() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::Repaint() { @@ -1093,12 +1100,12 @@ void Panel::Repaint() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::Think() { if (IsVisible()) - { + { // update any tooltips if (m_pTooltips) { @@ -1125,7 +1132,7 @@ void Panel::OnChildSettingsApplied( KeyValues *pInResourceData, Panel *pChild ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::PaintTraverse( bool repaint, bool allowForce ) { @@ -1254,17 +1261,17 @@ void Panel::PaintTraverse( bool repaint, bool allowForce ) // IsBuildGroupEnabled recurses up all the parents and ends up being very expensive as it wanders all over memory if ( GetBuildModeDialogCount() && IsBuildGroupEnabled() ) //&& HasFocus() ) { - // outline all selected panels - // outline all selected panels + // outline all selected panels + // outline all selected panels CUtlVector *controlGroup = _buildGroup->GetControlGroup(); for (int i=0; i < controlGroup->Size(); ++i) { surface()->PushMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel(), false ); ((*controlGroup)[i].Get())->PaintBuildOverlay(); surface()->PopMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel() ); - } - - _buildGroup->DrawRulers(); + } + + _buildGroup->DrawRulers(); } #endif @@ -1289,7 +1296,7 @@ void Panel::PaintTraverse( bool repaint, bool allowForce ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::PaintBorder() { @@ -1298,10 +1305,10 @@ void Panel::PaintBorder() //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::PaintBackground() -{ +{ int wide, tall; GetSize( wide, tall ); if ( m_SkipChild.Get() && m_SkipChild->IsVisible() ) @@ -1357,7 +1364,7 @@ void Panel::PaintBackground() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::Paint() { @@ -1366,13 +1373,13 @@ void Panel::Paint() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::PostChildPaint() { // Empty on purpose // This is called if _postChildPaintEnabled is true and allows painting to - // continue on the surface after all of the panel's children have painted + // continue on the surface after all of the panel's children have painted // themselves. Allows drawing an overlay on top of the children, etc. } @@ -1445,7 +1452,7 @@ void Panel::SetParent(Panel *newParent) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetParent(VPANEL newParent) { @@ -1480,7 +1487,7 @@ void Panel::SetParent(VPANEL newParent) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::OnChildAdded(VPANEL child) { @@ -1593,7 +1600,7 @@ Panel *Panel::FindChildByName(const char *childName, bool recurseDown) if ( idx != m_dictChidlren.InvalidIndex() ) { Panel *pCachedChild = ipanel()->GetPanel( m_dictChidlren[ idx ], GetControlsModuleName() ); - + if ( !pCachedChild ) { m_dictChidlren.Remove( childName ); @@ -1678,7 +1685,7 @@ void Panel::SetAutoDelete( bool state ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool Panel::IsAutoDeleteSet() { @@ -1702,17 +1709,30 @@ void Panel::DeletePanel() //----------------------------------------------------------------------------- HScheme Panel::GetScheme() { + HScheme iScheme; + if (m_iScheme) { - return m_iScheme; // return our internal scheme + iScheme = m_iScheme; // return our internal scheme } - - if (GetVParent()) // recurse down the heirarchy + else if (GetVParent()) // recurse down the heirarchy { - return ipanel()->GetScheme(GetVParent()); + iScheme = ipanel()->GetScheme(GetVParent()); + } + else { + iScheme = scheme()->GetDefaultScheme(); } - return scheme()->GetDefaultScheme(); +#ifdef MAPBASE + // If a custom client scheme is available, use the custom scheme. + // TODO: Need a better way to detect that this panel actually uses ClientScheme.res + if (g_iCustomClientSchemeOverride != 0 && iScheme == scheme()->GetScheme( "ClientScheme" ) && vgui_mapbase_custom_schemes.GetBool()) + { + return g_iCustomClientSchemeOverride; + } +#endif // MAPBASE + + return iScheme; } //----------------------------------------------------------------------------- @@ -1727,7 +1747,7 @@ void Panel::SetScheme(const char *tag) } //----------------------------------------------------------------------------- -// Purpose: set the scheme to render this panel with +// Purpose: set the scheme to render this panel with //----------------------------------------------------------------------------- void Panel::SetScheme(HScheme scheme) { @@ -1779,7 +1799,7 @@ void Panel::InternalCursorMoved(int x, int y) if ( IsCursorNone() ) return; - + if ( !IsMouseInputEnabled() ) { return; @@ -1811,7 +1831,7 @@ void Panel::InternalCursorEntered() { if (IsCursorNone() || !IsMouseInputEnabled()) return; - + if (IsBuildGroupEnabled()) return; @@ -1833,7 +1853,7 @@ void Panel::InternalCursorExited() { if (IsCursorNone() || !IsMouseInputEnabled()) return; - + if (IsBuildGroupEnabled()) return; @@ -1859,8 +1879,8 @@ bool Panel::IsChildOfSurfaceModalPanel() //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsChildOfModalSubTree() @@ -1877,7 +1897,7 @@ bool Panel::IsChildOfModalSubTree() //----------------------------------------------------------------------------- // Purpose: Checks to see if message is being subverted due to modal subtree logic -// Input : - +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- static bool ShouldHandleInputMessage( VPANEL p ) @@ -1922,7 +1942,7 @@ void Panel::InternalMousePressed(int code) } } - // The menu system passively watches for mouse released messages so it + // The menu system passively watches for mouse released messages so it // can clear any open menus if the release is somewhere other than on a menu Menu::OnInternalMousePressed( this, (MouseCode)code ); @@ -1931,7 +1951,7 @@ void Panel::InternalMousePressed(int code) if ( IsCursorNone() ) return; - + if ( !IsMouseInputEnabled()) { #if defined( VGUI_USEDRAGDROP ) @@ -1939,7 +1959,7 @@ void Panel::InternalMousePressed(int code) #endif return; } - + if (IsBuildGroupEnabled()) { if ( _buildGroup->MousePressed((MouseCode)code, this) ) @@ -1950,7 +1970,7 @@ void Panel::InternalMousePressed(int code) #ifdef STAGING_ONLY // If holding CTRL + ALT, invalidate layout. For debugging purposes - if ( ( vgui::input()->IsKeyDown(KEY_LCONTROL) || vgui::input()->IsKeyDown(KEY_RCONTROL) ) + if ( ( vgui::input()->IsKeyDown(KEY_LCONTROL) || vgui::input()->IsKeyDown(KEY_RCONTROL) ) && ( vgui::input()->IsKeyDown(KEY_LALT) || vgui::input()->IsKeyDown(KEY_RALT) ) ) { InvalidateLayout( true, true ); @@ -1992,12 +2012,12 @@ void Panel::InternalMouseDoublePressed(int code) if ( IsCursorNone() ) return; - + if ( !IsMouseInputEnabled()) { return; } - + if (IsBuildGroupEnabled()) { if ( _buildGroup->MouseDoublePressed((MouseCode)code, this) ) @@ -2049,7 +2069,7 @@ void Panel::InternalMouseTriplePressed( int code ) if ( IsCursorNone() ) return; - + if ( !IsMouseInputEnabled()) { #if defined( VGUI_USEDRAGDROP ) @@ -2057,7 +2077,7 @@ void Panel::InternalMouseTriplePressed( int code ) #endif return; } - + if (IsBuildGroupEnabled()) { return; @@ -2088,12 +2108,12 @@ void Panel::InternalMouseReleased(int code) if ( IsCursorNone() ) return; - + if ( !IsMouseInputEnabled()) { return; } - + if (IsBuildGroupEnabled()) { if ( _buildGroup->MouseReleased((MouseCode)code, this) ) @@ -2143,7 +2163,7 @@ void Panel::InternalKeyCodePressed(int code) if ( !ShouldHandleInputMessage() ) return; - if (IsKeyBoardInputEnabled()) + if (IsKeyBoardInputEnabled()) { OnKeyCodePressed((KeyCode)code); } @@ -2155,10 +2175,10 @@ void Panel::InternalKeyCodePressed(int code) #if defined( VGUI_USEKEYBINDINGMAPS ) //----------------------------------------------------------------------------- -// Purpose: -// Input : *bindingName - -// keycode - -// modifiers - +// Purpose: +// Input : *bindingName - +// keycode - +// modifiers - //----------------------------------------------------------------------------- void Panel::AddKeyBinding( char const *bindingName, int keycode, int modifiers ) { @@ -2167,15 +2187,15 @@ void Panel::AddKeyBinding( char const *bindingName, int keycode, int modifiers ) { Assert( 0 ); return; - } + } - BoundKey_t kb; - kb.isbuiltin = false; - kb.bindingname = CopyString( bindingName ); - kb.keycode = keycode; - kb.modifiers = modifiers; + BoundKey_t kb; + kb.isbuiltin = false; + kb.bindingname = CopyString( bindingName ); + kb.keycode = keycode; + kb.modifiers = modifiers; - map->boundkeys.AddToTail( kb ); + map->boundkeys.AddToTail( kb ); } KeyBindingMap_t *Panel::LookupBinding( char const *bindingName ) @@ -2304,8 +2324,8 @@ void Panel::RemoveAllKeyBindings() } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - //----------------------------------------------------------------------------- void Panel::ReloadKeyBindings() { @@ -2482,7 +2502,7 @@ static void AddModifierToString( char const *modifiername, char *buf, size_t buf } Q_strncat( buf, add, bufsize, COPY_ALL_CHARACTERS ); - + } wchar_t const *Panel::KeyCodeModifiersToDisplayString( KeyCode code, int modifiers ) @@ -2543,9 +2563,9 @@ static void WriteKeyBindingToBuffer( CUtlBuffer& buf, int level, const BoundKey_ } //----------------------------------------------------------------------------- -// Purpose: -// Input : *filename - -// *pathID - +// Purpose: +// Input : *filename - +// *pathID - //----------------------------------------------------------------------------- void Panel::SaveKeyBindingsToBuffer( int level, CUtlBuffer& buf ) { @@ -2564,7 +2584,7 @@ void Panel::SaveKeyBindingsToBuffer( int level, CUtlBuffer& buf ) // Spew to file BufPrint( buf, level, "\"%s\"\n", binding.bindingname ); BufPrint( buf, level, "{\n" ); - + WriteKeyBindingToBuffer( buf, level + 1, binding ); BufPrint( buf, level, "}\n" ); @@ -2637,7 +2657,7 @@ bool Panel::ParseKeyBindings( KeyValues *kv ) for( int i = 0; i < c ; ++i ) { KeyBindingMap_t *binding = &map->entries[ i ]; - + // See if there is a bound key CUtlVector< BoundKey_t * > list; LookupBoundKeys( binding->bindingname, list ); @@ -2667,8 +2687,8 @@ bool Panel::ParseKeyBindings( KeyValues *kv ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : handle - +// Purpose: +// Input : handle - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- void Panel::SetKeyBindingsContext( KeyBindingContextHandle_t handle ) @@ -2679,8 +2699,8 @@ void Panel::SetKeyBindingsContext( KeyBindingContextHandle_t handle ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : KeyBindingContextHandle_t //----------------------------------------------------------------------------- KeyBindingContextHandle_t Panel::GetKeyBindingsContext() const @@ -2689,8 +2709,8 @@ KeyBindingContextHandle_t Panel::GetKeyBindingsContext() const } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsValidKeyBindingsContext() const @@ -2718,7 +2738,7 @@ void Panel::EditKeyBindings() //----------------------------------------------------------------------------- // Purpose: Set this to false to disallow IsKeyRebound chaining to GetParent() Panels... -// Input : state - +// Input : state - //----------------------------------------------------------------------------- void Panel::SetAllowKeyBindingChainToParent( bool state ) { @@ -2727,8 +2747,8 @@ void Panel::SetAllowKeyBindingChainToParent( bool state ) //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsKeyBindingChainToParentAllowed() const @@ -2745,7 +2765,7 @@ bool Panel::IsKeyOverridden( KeyCode code, int modifiers ) bool Panel::IsKeyRebound( KeyCode code, int modifiers ) { if ( IsKeyBoardInputEnabled() ) - { + { KeyBindingMap_t* binding = LookupBindingByKeyCode( code, modifiers ); // Only dispatch if we're part of the current modal subtree if ( binding && IsChildOfSurfaceModalPanel() ) @@ -2789,7 +2809,7 @@ void Panel::InternalKeyCodeTyped( int code ) return; } - if (IsKeyBoardInputEnabled()) + if (IsKeyBoardInputEnabled()) { bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); @@ -2864,7 +2884,7 @@ void Panel::InternalKeyCodeReleased(int code) if ( !ShouldHandleInputMessage() ) return; - if (IsKeyBoardInputEnabled()) + if (IsKeyBoardInputEnabled()) { if (IsBuildGroupEnabled()) { @@ -2886,7 +2906,7 @@ void Panel::InternalKeyFocusTicked() { if (IsBuildGroupEnabled()) return; - + OnKeyFocusTicked(); } @@ -2927,17 +2947,17 @@ void Panel::InternalSetCursor() visible &= ipanel()->IsVisible(p); p = ipanel()->GetParent(p); } - + // only change the cursor if this panel is visible, and if its part of the main VGUI tree - if (visible && HasParent(surface()->GetEmbeddedPanel())) - { + if (visible && HasParent(surface()->GetEmbeddedPanel())) + { HCursor cursor = GetCursor(); - + if (IsBuildGroupEnabled()) { cursor = _buildGroup->GetCursor(this); } - + if (input()->GetCursorOveride()) { cursor = input()->GetCursorOveride(); @@ -2954,7 +2974,7 @@ void Panel::InternalSetCursor() void Panel::OnThink() { #if defined( VGUI_USEDRAGDROP ) - if ( IsPC() && + if ( IsPC() && m_pDragDrop->m_bDragEnabled && m_pDragDrop->m_bDragging && m_pDragDrop->m_bDragStarted ) @@ -2992,7 +3012,7 @@ void Panel::OnThink() m_pDragDrop->m_bDropMenuShown = true; CUtlVector< KeyValues * > data; - + GetDragData( data ); int x, y; @@ -3004,7 +3024,7 @@ void Panel::OnThink() } Menu *menu = new Menu( m_pDragDrop->m_hCurrentDrop.Get(), "DropContext" ); - + bool useMenu = m_pDragDrop->m_hCurrentDrop->GetDropContextMenu( menu, data ); if ( useMenu ) { @@ -3488,7 +3508,7 @@ void Panel::SetBuildModeDeletable(bool state) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool Panel::IsBuildModeActive() { @@ -3496,7 +3516,7 @@ bool Panel::IsBuildModeActive() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::GetClipRect(int& x0,int& y0,int& x1,int& y1) { @@ -3504,7 +3524,7 @@ void Panel::GetClipRect(int& x0,int& y0,int& x1,int& y1) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- int Panel::GetChildCount() { @@ -3545,7 +3565,7 @@ bool Panel::RequestFocusPrev(VPANEL panel) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- bool Panel::RequestFocusNext(VPANEL panel) { @@ -3579,7 +3599,7 @@ void Panel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- VPANEL Panel::GetCurrentKeyFocus() { @@ -3599,7 +3619,7 @@ bool Panel::HasFocus() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetTabPosition(int position) { @@ -3607,7 +3627,7 @@ void Panel::SetTabPosition(int position) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- int Panel::GetTabPosition() { @@ -3615,7 +3635,7 @@ int Panel::GetTabPosition() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::InternalFocusChanged(bool lost) { @@ -3640,7 +3660,7 @@ void Panel::OnMouseCaptureLost() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::AddActionSignalTarget(Panel *messageTarget) { @@ -3652,7 +3672,7 @@ void Panel::AddActionSignalTarget(Panel *messageTarget) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::AddActionSignalTarget(VPANEL messageTarget) { @@ -3664,7 +3684,7 @@ void Panel::AddActionSignalTarget(VPANEL messageTarget) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::RemoveActionSignalTarget(Panel *oldTarget) { @@ -3677,7 +3697,7 @@ void Panel::RemoveActionSignalTarget(Panel *oldTarget) void Panel::PostActionSignal( KeyValues *message ) { if ( m_bIsSilent != true ) - { + { // add who it was from the message message->SetPtr("panel", this); int i; @@ -3864,12 +3884,12 @@ void Panel::PerformLayout() void Panel::InvalidateLayout( bool layoutNow, bool reloadScheme ) { _flags.SetFlag( NEEDS_LAYOUT ); - + if (reloadScheme) { // make all our children reload the scheme _flags.SetFlag( NEEDS_SCHEME_UPDATE ); - + for (int i = 0; i < GetChildCount(); i++) { vgui::Panel* panel = GetChild(i); @@ -3878,10 +3898,10 @@ void Panel::InvalidateLayout( bool layoutNow, bool reloadScheme ) panel->InvalidateLayout(layoutNow, true); } } - + PerformApplySchemeSettings(); } - + if (layoutNow) { InternalPerformLayout(); @@ -3897,7 +3917,7 @@ bool Panel::IsCursorNone() { return true; } - + return false; } @@ -3985,7 +4005,7 @@ bool Panel::IsLayoutInvalid() //----------------------------------------------------------------------------- // Sets the pin corner + resize mode for resizing panels //----------------------------------------------------------------------------- -void Panel::SetAutoResize( PinCorner_e pinCorner, AutoResize_e resizeDir, +void Panel::SetAutoResize( PinCorner_e pinCorner, AutoResize_e resizeDir, int nPinOffsetX, int nPinOffsetY, int nUnpinnedCornerOffsetX, int nUnpinnedCornerOffsetY ) { _pinCorner = pinCorner; @@ -4010,7 +4030,7 @@ void Panel::SetPinCorner( PinCorner_e pinCorner, int nOffsetX, int nOffsetY ) m_nResizeDeltaY = 0; } - + //----------------------------------------------------------------------------- // Purpose: data accessor //----------------------------------------------------------------------------- @@ -4077,7 +4097,7 @@ void Panel::PinToSibling( const char *pszSibling, PinCorner_e pinOurCorner, PinC } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::UpdateSiblingPin( void ) { @@ -4105,7 +4125,7 @@ void Panel::UpdateSiblingPin( void ) //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::ApplySchemeSettings(IScheme *pScheme) { @@ -4147,7 +4167,7 @@ void Panel::PerformApplySchemeSettings() if ( pScheme ) // this should NEVER be null, but if it is bad things would happen in ApplySchemeSettings... { ApplySchemeSettings( pScheme ); - //_needsSchemeUpdate = false; + //_needsSchemeUpdate = false; ApplyOverridableColors(); @@ -4370,7 +4390,7 @@ void Panel::ApplySettings(KeyValues *inResourceData) const char *ystr = inResourceData->GetString( "ypos", NULL ); _buildModeFlags |= ComputePos( this, xstr, x, wide, alignScreenWide, true, OP_SET ); _buildModeFlags |= ComputePos( this, ystr, y, tall, alignScreenTall, false, OP_SET ); - + bool bUsesTitleSafeArea = false; int titleSafeWide = 0; @@ -4583,10 +4603,10 @@ void Panel::ApplySettings(KeyValues *inResourceData) AddActionSignalTarget( pActionSignalTarget ); } - // check to see if we need to render to the frame buffer even if + // check to see if we need to render to the frame buffer even if // stereo mode is trying to render all of the ui to a render target m_bForceStereoRenderToFrameBuffer = inResourceData->GetBool( "ForceStereoRenderToFrameBuffer", false ); - + //============================================================================= // HPE_BEGIN: // [pfreese] Support for reading rounded corner flags @@ -4790,7 +4810,7 @@ void Panel::ApplyOverridableColors( void ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetOverridableColor( Color *pColor, const Color &newColor ) { @@ -4808,7 +4828,7 @@ void Panel::SetOverridableColor( Color *pColor, const Color &newColor ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Color Panel::GetSchemeColor(const char *keyName, IScheme *pScheme) { @@ -4816,7 +4836,7 @@ Color Panel::GetSchemeColor(const char *keyName, IScheme *pScheme) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Color Panel::GetSchemeColor(const char *keyName, Color defaultColor, IScheme *pScheme) { @@ -4857,7 +4877,7 @@ bool Panel::HasUserConfigSettings() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::InternalInvalidateLayout() { @@ -4872,7 +4892,7 @@ void Panel::OnMove() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::InternalMove() { @@ -4956,7 +4976,7 @@ void PreparePanelMessageMap(PanelMessageMap *panelMap) item->secondParamSymbol = INVALID_KEY_SYMBOL; } } - + panelMap->processed = true; panelMap = panelMap->baseMap; } @@ -5003,7 +5023,7 @@ void Panel::OnMessage(const KeyValues *params, VPANEL ifromPanel) (this->*(pMap->func))(); break; } - + case 1: { KeyValues *param1 = params->FindKey(pMap->firstParamSymbol); @@ -5276,7 +5296,7 @@ void Panel::OnOldMessage(KeyValues *params, VPANEL ifromPanel) typedef void (Panel::*MessageFunc_Ptr_t)(void *); (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)params->GetPtr(pMessageMap[i].firstParamName) ); break; - + case DATATYPE_HANDLE: { typedef void (Panel::*MessageFunc_Ptr_t)(void *); @@ -5496,7 +5516,7 @@ void Panel::PreparePanelMap( PanelMap_t *panelMap ) item->secondParamSymbol = INVALID_KEY_SYMBOL; } } - + panelMap->processed = true; panelMap = panelMap->baseMap; } @@ -5547,7 +5567,7 @@ Panel *PHandle::Set(Panel *pent) { m_iPanelID = INVALID_PANEL; } - return pent; + return pent; } Panel *PHandle::Set( HPanel hPanel ) @@ -5585,7 +5605,7 @@ VPANEL VPanelHandle::Set(VPANEL pent) { m_iPanelID = INVALID_PANEL; } - return pent; + return pent; } //----------------------------------------------------------------------------- @@ -5597,7 +5617,7 @@ BaseTooltip *Panel::GetTooltip() { m_pTooltips = new TextTooltip(this, NULL); m_bToolTipOverridden = false; - + if ( IsConsoleStylePanel() ) { m_pTooltips->SetEnabled( false ); @@ -5608,7 +5628,7 @@ BaseTooltip *Panel::GetTooltip() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetTooltip( BaseTooltip *pToolTip, const char *pszText ) { @@ -5657,11 +5677,11 @@ const char *Panel::GetEffectiveTooltipText() const // Purpose: sets the proportional flag on this panel and all it's children //----------------------------------------------------------------------------- void Panel::SetProportional(bool state) -{ +{ // only do something if the state changes if( state != _flags.IsFlagSet( IS_PROPORTIONAL ) ) { - _flags.SetFlag( IS_PROPORTIONAL, state ); + _flags.SetFlag( IS_PROPORTIONAL, state ); for(int i=0;im_pfnLookup)( panel ) ); kv->SetFloat( entry->name(), *(float *)data ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -5753,7 +5773,7 @@ class CProportionalFloatProperty : public vgui::IPanelAnimationPropertyConverter f = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), f ); kv->SetFloat( entry->name(), f ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -5779,7 +5799,7 @@ class CIntProperty : public vgui::IPanelAnimationPropertyConverter void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); kv->SetInt( entry->name(), *(int *)data ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -5803,7 +5823,7 @@ class CProportionalIntProperty : public vgui::IPanelAnimationPropertyConverter i = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), i ); kv->SetInt( entry->name(), i ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -5970,7 +5990,7 @@ class CColorProperty : public vgui::IPanelAnimationPropertyConverter void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); kv->SetColor( entry->name(), *(Color *)data ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); @@ -6011,7 +6031,7 @@ class CBoolProperty : public vgui::IPanelAnimationPropertyConverter void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); kv->SetInt( entry->name(), *(bool *)data ? 1 : 0 ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -6040,7 +6060,7 @@ class CStringProperty : public vgui::IPanelAnimationPropertyConverter void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); kv->SetString( entry->name(), (char *)data ); } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -6068,7 +6088,7 @@ class CHFontProperty : public vgui::IPanelAnimationPropertyConverter kv->SetString( entry->name(), fontName ); } } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); @@ -6113,7 +6133,7 @@ class CTextureIdProperty : public vgui::IPanelAnimationPropertyConverter kv->SetString( entry->name(), "" ); } } - + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) { void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); @@ -6309,7 +6329,7 @@ void Panel::InternalApplySettings( PanelAnimationMap *map, KeyValues *inResource // Loop through keys KeyValues *kv; - + for ( kv = inResourceData->GetFirstSubKey(); kv; kv = kv->GetNextKey() ) { char const *varname = kv->GetName(); @@ -6348,14 +6368,14 @@ void Panel::InternalInitDefaultValues( PanelAnimationMap *map ) } if ( map->baseMap ) - { + { InternalInitDefaultValues( map->baseMap ); } } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - //----------------------------------------------------------------------------- int Panel::GetPaintBackgroundType() { @@ -6363,9 +6383,9 @@ int Panel::GetPaintBackgroundType() } //----------------------------------------------------------------------------- -// Purpose: -// Input : w - -// h - +// Purpose: +// Input : w - +// h - //----------------------------------------------------------------------------- void Panel::GetCornerTextureSize( int& w, int& h ) { @@ -6560,13 +6580,13 @@ void Panel::DrawBoxFade(int x, int y, int wide, int tall, Color color, float nor } //----------------------------------------------------------------------------- -// Purpose: -// Input : x - -// y - -// wide - -// tall - -// color - -// normalizedAlpha - +// Purpose: +// Input : x - +// y - +// wide - +// tall - +// color - +// normalizedAlpha - //----------------------------------------------------------------------------- void Panel::DrawHollowBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha ) { @@ -6630,13 +6650,13 @@ void Panel::DrawTexturedBox(int x, int y, int wide, int tall, Color color, float //----------------------------------------------------------------------------- // Purpose: Marks this panel as draggable (note that children will chain to their parents to see if any parent is draggable) -// Input : enabled - +// Input : enabled - //----------------------------------------------------------------------------- void Panel::SetDragEnabled( bool enabled ) { #if defined( VGUI_USEDRAGDROP ) // If turning it off, quit dragging if mid-drag - if ( !enabled && + if ( !enabled && m_pDragDrop->m_bDragging ) { OnFinishDragging( false, (MouseCode)-1 ); @@ -6646,8 +6666,8 @@ void Panel::SetDragEnabled( bool enabled ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsDragEnabled() const @@ -6675,8 +6695,8 @@ void Panel::SetBlockDragChaining( bool block ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsBlockingDragChaining() const @@ -6709,7 +6729,7 @@ void Panel::SetDragSTartTolerance( int nTolerance ) //----------------------------------------------------------------------------- // Purpose: Marks this panel as droppable ( note that children will chain to their parents to see if any parent is droppable) -// Input : enabled - +// Input : enabled - //----------------------------------------------------------------------------- void Panel::SetDropEnabled( bool enabled, float flHoverContextTime /* = 0.0f */ ) { @@ -6720,8 +6740,8 @@ void Panel::SetDropEnabled( bool enabled, float flHoverContextTime /* = 0.0f */ } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsDropEnabled() const @@ -6733,17 +6753,17 @@ bool Panel::IsDropEnabled() const } //----------------------------------------------------------------------------- -// Purpose: Chains up to any parent -// 1) marked DropEnabled; and +// Purpose: Chains up to any parent +// 1) marked DropEnabled; and // 2) willing to accept the drop payload -// Input : - +// Input : - // Output : Panel //----------------------------------------------------------------------------- Panel *Panel::GetDropTarget( CUtlVector< KeyValues * >& msglist ) { #if defined( VGUI_USEDRAGDROP ) // Found one - if ( m_pDragDrop->m_bDropEnabled && + if ( m_pDragDrop->m_bDropEnabled && IsDroppable( msglist ) ) { return this; @@ -6761,7 +6781,7 @@ Panel *Panel::GetDropTarget( CUtlVector< KeyValues * >& msglist ) //----------------------------------------------------------------------------- // Purpose: Chains up to first parent marked DragEnabled -// Input : - +// Input : - // Output : Panel //----------------------------------------------------------------------------- Panel *Panel::GetDragPanel() @@ -6785,8 +6805,8 @@ Panel *Panel::GetDragPanel() } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - //----------------------------------------------------------------------------- void Panel::OnStartDragging() { @@ -6816,15 +6836,15 @@ void Panel::OnStartDragging() //----------------------------------------------------------------------------- // Purpose: Called if drag drop is started but not dropped on top of droppable panel... -// Input : - +// Input : - //----------------------------------------------------------------------------- void Panel::OnDragFailed( CUtlVector< KeyValues * >& msglist ) { } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - //----------------------------------------------------------------------------- void Panel::OnFinishDragging( bool mousereleased, MouseCode code, bool abort /*= false*/ ) { @@ -6968,8 +6988,8 @@ void Panel::OnDropContextHoverHide( CUtlVector< KeyValues * >& msglist ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *msg - +// Purpose: +// Input : *msg - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsDroppable( CUtlVector< KeyValues * >& msglist ) @@ -6978,11 +6998,11 @@ bool Panel::IsDroppable( CUtlVector< KeyValues * >& msglist ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : startx - -// starty - -// mx - -// my - +// Purpose: +// Input : startx - +// starty - +// mx - +// my - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::CanStartDragging( int startx, int starty, int mx, int my ) @@ -7028,8 +7048,8 @@ bool IsSelfDroppable( CUtlVector< KeyValues * > &dragData ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - //----------------------------------------------------------------------------- void Panel::OnContinueDragging() { @@ -7157,8 +7177,8 @@ void Panel::OnContinueDragging() #if defined( VGUI_USEDRAGDROP ) //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : DragDrop_t //----------------------------------------------------------------------------- DragDrop_t *Panel::GetDragDropInfo() @@ -7175,7 +7195,7 @@ void Panel::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) //----------------------------------------------------------------------------- // Purpose: Virtual method to allow panels to add to the default values -// Input : *msg - +// Input : *msg - //----------------------------------------------------------------------------- void Panel::OnCreateDragData( KeyValues *msg ) { @@ -7234,8 +7254,8 @@ void Panel::CreateDragData() } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : KeyValues //----------------------------------------------------------------------------- void Panel::GetDragData( CUtlVector< KeyValues * >& list ) @@ -7351,8 +7371,8 @@ void CDragDropHelperPanel::AddPanel( Panel *current ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : *search - +// Purpose: +// Input : *search - //----------------------------------------------------------------------------- void CDragDropHelperPanel::RemovePanel( Panel *search ) { @@ -7369,10 +7389,10 @@ void CDragDropHelperPanel::RemovePanel( Panel *search ) #endif //----------------------------------------------------------------------------- // Purpose: Enumerates panels under mouse x,y -// Input : panelList - -// x - -// y - -// check - +// Input : panelList - +// x - +// y - +// check - //----------------------------------------------------------------------------- void Panel::FindDropTargetPanel_R( CUtlVector< VPANEL >& panelList, int x, int y, VPANEL check ) { @@ -7396,8 +7416,8 @@ void Panel::FindDropTargetPanel_R( CUtlVector< VPANEL >& panelList, int x, int y } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Panel //----------------------------------------------------------------------------- Panel *Panel::FindDropTargetPanel() @@ -7461,7 +7481,7 @@ Panel *Panel::FindDropTargetPanel() //----------------------------------------------------------------------------- // Purpose: Mouse is on draggable panel and has started moving, but is not over a droppable panel yet -// Input : - +// Input : - //----------------------------------------------------------------------------- void Panel::OnDraggablePanelPaint() { @@ -7497,7 +7517,7 @@ void Panel::OnDraggablePanelPaint() //----------------------------------------------------------------------------- // Purpose: Mouse is now over a droppable panel -// Input : *dragPanel - +// Input : *dragPanel - //----------------------------------------------------------------------------- void Panel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) { @@ -7521,8 +7541,8 @@ void Panel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVecto } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Color //----------------------------------------------------------------------------- Color Panel::GetDropFrameColor() @@ -7534,8 +7554,8 @@ Color Panel::GetDropFrameColor() } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Color //----------------------------------------------------------------------------- Color Panel::GetDragFrameColor() @@ -7547,8 +7567,8 @@ Color Panel::GetDragFrameColor() } //----------------------------------------------------------------------------- -// Purpose: -// Input : *data - +// Purpose: +// Input : *data - //----------------------------------------------------------------------------- void Panel::OnPanelDropped( CUtlVector< KeyValues * >& data ) { @@ -7572,8 +7592,8 @@ void Panel::OnPanelExitedDroppablePanel ( CUtlVector< KeyValues * >& msglist ) } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - //----------------------------------------------------------------------------- void Panel::DragDropStartDragging() { @@ -7612,8 +7632,8 @@ void Panel::DragDropStartDragging() } //----------------------------------------------------------------------------- -// Purpose: -// Input : - +// Purpose: +// Input : - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Panel::IsBeingDragged() @@ -7720,8 +7740,8 @@ void Panel::FillRectSkippingPanel( const Color &clr, int x, int y, int w, int h, //----------------------------------------------------------------------------- -// Purpose: -// Input : *child - +// Purpose: +// Input : *child - //----------------------------------------------------------------------------- void Panel::SetSkipChildDuringPainting( Panel *child ) { @@ -7733,7 +7753,7 @@ HPanel Panel::ToHandle() const return ivgui()->PanelToHandle( _vpanel ); } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::NavigateUp() { @@ -7749,7 +7769,7 @@ Panel* Panel::NavigateUp() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::NavigateDown() { @@ -7765,7 +7785,7 @@ Panel* Panel::NavigateDown() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::NavigateLeft() { @@ -7780,7 +7800,7 @@ Panel* Panel::NavigateLeft() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::NavigateRight() { @@ -7819,7 +7839,7 @@ Panel* Panel::NavigateBack() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::NavigateTo() { @@ -7840,7 +7860,7 @@ void Panel::NavigateTo() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::NavigateFrom() { @@ -7875,7 +7895,7 @@ void Panel::NavigateToChild( Panel *pNavigateTo ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::SetNavUp( Panel* navUp ) { @@ -7891,7 +7911,7 @@ Panel* Panel::SetNavUp( Panel* navUp ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::SetNavDown( Panel* navDown ) { @@ -7907,7 +7927,7 @@ Panel* Panel::SetNavDown( Panel* navDown ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::SetNavLeft( Panel* navLeft ) { @@ -7923,7 +7943,7 @@ Panel* Panel::SetNavLeft( Panel* navLeft ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel* Panel::SetNavRight( Panel* navRight ) { @@ -7963,7 +7983,7 @@ Panel* Panel::SetNavBack( Panel* navBack ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- Panel::NAV_DIRECTION Panel::GetLastNavDirection() { @@ -7971,21 +7991,21 @@ Panel::NAV_DIRECTION Panel::GetLastNavDirection() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::OnNavigateTo( const char* panelName ) { } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::OnNavigateFrom( const char* panelName ) { } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetNavUp( const char* controlName ) { @@ -7997,7 +8017,7 @@ void Panel::SetNavUp( const char* controlName ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetNavDown( const char* controlName ) { @@ -8008,7 +8028,7 @@ void Panel::SetNavDown( const char* controlName ) } } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetNavLeft( const char* controlName ) { @@ -8020,7 +8040,7 @@ void Panel::SetNavLeft( const char* controlName ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void Panel::SetNavRight( const char* controlName ) { @@ -8059,7 +8079,7 @@ void Panel::SetNavBack( const char* controlName ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- vgui::Panel* Panel::GetNavUp( Panel *first ) { @@ -8365,7 +8385,7 @@ class CPanelMessageMapDictionary }; char const *StripNamespace( char const *className ); - + CUtlDict< PanelMessageMapDictionaryEntry, int > m_MessageMaps; CUtlMemoryPool m_PanelMessageMapPool; }; @@ -8395,7 +8415,7 @@ PanelMessageMap *CPanelMessageMapDictionary::FindPanelMessageMap( char const *cl #include //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- PanelMessageMap *CPanelMessageMapDictionary::FindOrAddPanelMessageMap( char const *className ) { @@ -8434,7 +8454,7 @@ class CPanelKeyBindingMapDictionary }; char const *StripNamespace( char const *className ); - + CUtlDict< PanelKeyBindingMapDictionaryEntry, int > m_MessageMaps; CUtlMemoryPool m_PanelKeyBindingMapPool; }; @@ -8464,7 +8484,7 @@ PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindPanelKeyBindingMap( char #include //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindOrAddPanelKeyBindingMap( char const *className ) { @@ -8500,7 +8520,7 @@ namespace vgui { //----------------------------------------------------------------------------- - // Purpose: + // Purpose: //----------------------------------------------------------------------------- PanelMessageMap *FindOrAddPanelMessageMap( char const *className ) { @@ -8522,7 +8542,7 @@ namespace vgui return dictionary; } //----------------------------------------------------------------------------- - // Purpose: + // Purpose: //----------------------------------------------------------------------------- PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className ) { @@ -8559,7 +8579,7 @@ void VguiPanelGetSortedChildPanelList( Panel *pParentPanel, void *pSortedPanels } } -void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ ) +void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, const char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ ) { CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); diff --git a/vgui2/vgui_controls/ScrollBarSlider.cpp b/vgui2/vgui_controls/ScrollBarSlider.cpp index 32df2fae6..8e7657eb7 100644 --- a/vgui2/vgui_controls/ScrollBarSlider.cpp +++ b/vgui2/vgui_controls/ScrollBarSlider.cpp @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // // $NoKeywords: $ //=============================================================================// @@ -18,7 +18,9 @@ #include #include +#if _MSC_VER < 1900 #include +#endif // _MSC_VER // memdbgon must be the last include file in a .cpp file!!! #include @@ -30,7 +32,7 @@ using namespace vgui; //----------------------------------------------------------------------------- ScrollBarSlider::ScrollBarSlider(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName) { - _vertical=vertical; + _vertical=vertical; _dragging=false; _value=0; _range[0]=0; @@ -69,7 +71,7 @@ void ScrollBarSlider::SetValue(int value) if (value > _range[1] - _rangeWindow) { // note our scrolling range must take into acount _rangeWindow - value = _range[1] - _rangeWindow; + value = _range[1] - _rangeWindow; } if (value < _range[0]) @@ -95,7 +97,7 @@ int ScrollBarSlider::GetValue() } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void ScrollBarSlider::PerformLayout() { @@ -139,34 +141,34 @@ void ScrollBarSlider::RecomputeNobPosFromValue() width = ftall; length = fwide; } - + // our size is proportional to frangewindow/frange - // the scroll bar nob's length reflects the amount of stuff on the screen + // the scroll bar nob's length reflects the amount of stuff on the screen // vs the total amount of stuff we could scroll through in window // so if a window showed half its contents and the other half is hidden the // scroll bar's length is half the window. // if everything is on the screen no nob is displayed // frange is how many 'lines' of stuff we can display // frangewindow is how many 'lines' are in the display window - + // proportion of whole window that is on screen float proportion = frangewindow / frange; float fnobsize = length * proportion; if ( fnobsize < width ) fnobsize = (float)width; - + float freepixels = length - fnobsize; - + float firstpixel = freepixels * fper; - + _nobPos[0] = (int)( firstpixel ); _nobPos[1] = (int)( firstpixel + fnobsize ); - + if ( _nobPos[1] > length ) { _nobPos[0] = (int)( length - fnobsize ); _nobPos[1] = (int)length; } - + } Repaint(); @@ -206,18 +208,18 @@ void ScrollBarSlider::RecomputeValueFromNobPos() width = ftall; length = fwide; } - + // calculate the size of the nob float proportion = frangewindow / frange; float fnobsize = length * proportion; - + if ( fnobsize < width ) { fnobsize = width; } - + // Our scroll bar actually doesnt scroll through all frange lines in the truerange, we - // actually only scroll through frange-frangewindow number of lines so we must take that + // actually only scroll through frange-frangewindow number of lines so we must take that // into account when we calculate the value // convert to our local size system @@ -284,12 +286,12 @@ bool ScrollBarSlider::HasFullRange() return false; } - + //----------------------------------------------------------------------------- // Purpose: Inform other watchers that the ScrollBarSlider was moved //----------------------------------------------------------------------------- void ScrollBarSlider::SendScrollBarSliderMovedMessage() -{ +{ // send a changed message PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", _value)); } @@ -313,7 +315,7 @@ bool ScrollBarSlider::IsSliderVisible( void ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- void ScrollBarSlider::ApplySchemeSettings(IScheme *pScheme) { @@ -356,7 +358,7 @@ void ScrollBarSlider::Paint() int wide,tall; GetPaintSize(wide,tall); - if ( !IsSliderVisible() ) + if ( !IsSliderVisible() ) return; Color col = GetFgColor(); @@ -400,7 +402,7 @@ void ScrollBarSlider::Paint() void ScrollBarSlider::PaintBackground() { // BaseClass::PaintBackground(); - + int wide,tall; GetPaintSize(wide,tall); surface()->DrawSetColor(GetBgColor()); @@ -459,7 +461,7 @@ void ScrollBarSlider::OnCursorMoved(int x,int y) { _nobPos[0] = _nobDragStartPos[0] + (y - _dragStartPos[1]); _nobPos[1] = _nobDragStartPos[1] + (y - _dragStartPos[1]); - + if (_nobPos[1] > tall) { _nobPos[0] = tall - (_nobPos[1] - _nobPos[0]); @@ -471,13 +473,13 @@ void ScrollBarSlider::OnCursorMoved(int x,int y) { _nobPos[0] = _nobDragStartPos[0] + (x - _dragStartPos[0]); _nobPos[1] = _nobDragStartPos[1] + (x - _dragStartPos[0]); - + if (_nobPos[1] > wide) { _nobPos[0] = wide - (_nobPos[1] - _nobPos[0]); _nobPos[1] = wide; } - + } if (_nobPos[0] < 0) { @@ -485,7 +487,7 @@ void ScrollBarSlider::OnCursorMoved(int x,int y) _nobPos[0] = 0; SetValue(0); } - + InvalidateLayout(); // not invalidatelayout - because it won't draw while we're scrolling the slider RecomputeValueFromNobPos(); // Repaint(); diff --git a/vgui2/vgui_controls/TextImage.cpp b/vgui2/vgui_controls/TextImage.cpp index ead30ed24..5b47230cc 100644 --- a/vgui2/vgui_controls/TextImage.cpp +++ b/vgui2/vgui_controls/TextImage.cpp @@ -41,7 +41,7 @@ TextImage::TextImage(const char *text) : Image() { _utext = NULL; _textBufferLen = 0; - _font = INVALID_FONT; + _font = INVALID_FONT; _fallbackFont = INVALID_FONT; _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; _drawWidth = 0; @@ -55,7 +55,7 @@ TextImage::TextImage(const char *text) : Image() m_bUseFallbackFont = false; m_bRenderUsingFallbackFont = false; m_bAllCaps = false; - + SetText(text); // set the text. } @@ -106,7 +106,7 @@ void TextImage::SetText(const char *text) { // try lookup in localization tables _unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(text + 1); - + if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) { wchar_t *unicode = g_pVGuiLocalize->GetValueByIndex(_unlocalizedTextSymbol); @@ -326,7 +326,7 @@ void TextImage::Paint() { int wide, tall; GetSize(wide, tall); - + if (!_utext || GetFont() == INVALID_FONT ) return; @@ -535,12 +535,12 @@ void TextImage::GetTextSize(int &wide, int &tall) surface()->GetCharABCwide(font, ch, a, b, c); wide += (a + b + c); #endif - + if (ch == '\n') { tall += fontHeight; - if(wide>maxWide) + if(wide>maxWide) { maxWide=wide; } @@ -554,7 +554,7 @@ void TextImage::GetTextSize(int &wide, int &tall) if ( &text[i] == m_LineBreaks[j] ) { tall += fontHeight; - if(wide>maxWide) + if(wide>maxWide) { maxWide=wide; } @@ -562,7 +562,7 @@ void TextImage::GetTextSize(int &wide, int &tall) } } } - + } #ifdef OSX wide += 2; @@ -570,7 +570,7 @@ void TextImage::GetTextSize(int &wide, int &tall) wide += 3; #endif if (wide < maxWide) - { + { // maxWide only gets set if a newline is in the label wide = maxWide; } @@ -600,31 +600,31 @@ void TextImage::ResizeImageToContent() void TextImage::RecalculateNewLinePositions() { HFont font = GetFont(); - + int charWidth; int x = 0; - + //int wordStartIndex = 0; wchar_t *wordStartIndex = _utext; int wordLength = 0; bool hasWord = false; bool justStartedNewLine = true; bool wordStartedOnNewLine = true; - + int startChar = 0; - + // clear the line breaks list m_LineBreaks.RemoveAll(); m_LineXIndent.RemoveAll(); - + // handle the case where this char is a new line, in that case // we have already taken its break index into account above so skip it. - if (_utext[startChar] == '\r' || _utext[startChar] == '\n') + if (_utext[startChar] == '\r' || _utext[startChar] == '\n') { startChar++; } - - // loop through all the characters + + // loop through all the characters for (wchar_t *wsz = &_utext[startChar]; *wsz != 0; wsz++) { // handle stupid special characters, these should be removed @@ -640,7 +640,7 @@ void TextImage::RecalculateNewLinePositions() { ch = towupper( ch ); } - + // line break only on whitespace characters if (!iswspace(ch)) { @@ -660,7 +660,7 @@ void TextImage::RecalculateNewLinePositions() // end the word hasWord = false; } - + // get the width #if USE_GETKERNEDCHARWIDTH wchar_t chBefore = 0; @@ -678,13 +678,13 @@ void TextImage::RecalculateNewLinePositions() { justStartedNewLine = false; } - + // check to see if the word is past the end of the line [wordStartIndex, i) if ((x + charWidth) > _drawWidth || ch == '\r' || ch == '\n') { justStartedNewLine = true; hasWord = false; - + if (ch == '\r' || ch == '\n') { // set the break at the current character @@ -700,18 +700,18 @@ void TextImage::RecalculateNewLinePositions() { // set it at the last word Start m_LineBreaks.AddToTail(wordStartIndex); - + // just back to reparse the next line of text // ywb 8/1/07: Back off one extra char since the 'continue' will increment wsz for us by one in the for loop wsz = wordStartIndex - 1; } - + // reset word length wordLength = 0; x = 0; continue; } - + // add to the size x += charWidth; wordLength += charWidth; @@ -741,7 +741,7 @@ void TextImage::RecalculateEllipsesPosition() int h; GetSize( _drawWidth, h ); } - + for ( int check = 0; check < (m_bUseFallbackFont ? 2 : 1); ++check ) { HFont font = GetFont(); @@ -751,7 +751,7 @@ void TextImage::RecalculateEllipsesPosition() font = _fallbackFont; m_bRenderUsingFallbackFont = true; } - + int ellipsesWidth = 3 * surface()->GetCharacterWidth(font, '.'); int x = 0; @@ -966,7 +966,7 @@ void TextImage::RecalculateCenterWrapIndents() #else iCurLineW += surface()->GetCharacterWidth(font, ch); #endif - } + } // Add the final line int iIdx = m_LineXIndent.AddToTail(); @@ -987,3 +987,117 @@ void TextImage::SetColorChangeStream( CUtlSortVector *pOutCoords, bool bIgnoreEmptyLines ) +{ + HFont font = GetFont(); + if (!_utext || font == INVALID_FONT ) + return; + + // Early out if there's no newlines in our text + if (wcschr( _utext, L'\n' ) == NULL) + return; + + if (m_bRecalculateTruncation) + { + if ( m_bWrap || m_bWrapCenter ) + { + RecalculateNewLinePositions(); + } + + RecalculateEllipsesPosition(); + } + + int lineHeight = surface()->GetFontTall( GetFont() ); + float x = 0.0f; + int y = 0; + int iIndent = 0; + + int px, py; + GetPos(px, py); + + int currentLineBreak = 0; + + if ( m_bWrapCenter && m_LineXIndent.Count() ) + { + x = m_LineXIndent[0]; + } + + for (wchar_t *wsz = _utext; *wsz != 0; wsz++) + { + wchar_t ch = wsz[0]; + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + + // check for special characters + if ( ch == '\r' || ch <= 8 ) + { + // ignore, just use \n for newlines + continue; + } + else if (ch == '\n') + { + // newline + iIndent++; + if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) + { + x = m_LineXIndent[iIndent]; + } + else + { + x = 0; + } + y += lineHeight; + + if (!bIgnoreEmptyLines || (*(wsz + 1) != 0 && wsz[1] != '\n')) + { + pOutCoords->AddToTail( y ); + } + + continue; + } + else if (ch == '&') + { + // "&&" means draw a single ampersand, single one is a shortcut character + if (wsz[1] == '&') + { + // just move on and draw the second ampersand + wsz++; + } + } + + // see if we've hit the truncated portion of the string + if (wsz == m_pwszEllipsesPosition) + { + // do nothing + } + + if (currentLineBreak != m_LineBreaks.Count()) + { + if (wsz == m_LineBreaks[currentLineBreak]) + { + // newline + iIndent++; + if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) + { + x = m_LineXIndent[iIndent]; + } + else + { + x = 0; + } + + y += lineHeight; + currentLineBreak++; + } + } + + // Underlined text wants to draw the spaces anyway + x += surface()->GetCharacterWidth(font, ch); + } +} +#endif // MAPBASE diff --git a/vgui2/vgui_controls/Tooltip.cpp b/vgui2/vgui_controls/Tooltip.cpp index a16c5425c..895f03ec0 100644 --- a/vgui2/vgui_controls/Tooltip.cpp +++ b/vgui2/vgui_controls/Tooltip.cpp @@ -1,13 +1,13 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // This class is a message box that has two buttons, ok and cancel instead of // just the ok button of a message box. We use a message box class for the ok button // and implement another button here. //=============================================================================// -#include -#define PROTECTED_THINGS_DISABLE +// #include +// #define PROTECTED_THINGS_DISABLE #include #include @@ -35,7 +35,7 @@ static int s_iTooltipWindowCount = 0; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- -BaseTooltip::BaseTooltip(Panel *parent, const char *text) +BaseTooltip::BaseTooltip(Panel *parent, const char *text) { SetText(text); @@ -116,7 +116,7 @@ bool BaseTooltip::ShouldLayout( void ) return false; if (_delay > system()->GetTimeMillis()) - return false; + return false; // We only need to layout when we first become visible if ( !_isDirty ) @@ -156,7 +156,7 @@ void BaseTooltip::SetText(const char *text) m_Text.AddToTail(text[i]); } m_Text.AddToTail('\0'); - + if (s_TooltipWindow.Get() && m_pParent == s_TooltipWindow.Get()->GetParent()) { s_TooltipWindow->SetText(m_Text.Base()); @@ -223,7 +223,7 @@ void BaseTooltip::PositionWindow( Panel *pTipPanel ) // menu hanging up pTipPanel->SetPos(cursorX - iTipW, cursorY - iTipH - 20 ); } - } + } } @@ -252,7 +252,7 @@ TextTooltip::TextTooltip(Panel *parent, const char *text) : BaseTooltip( parent, s_TooltipWindow->MakePopup(false, true); s_TooltipWindow->SetKeyBoardInputEnabled( false ); s_TooltipWindow->SetMouseInputEnabled( false ); - + SetText(text); s_TooltipWindow->SetText(m_Text.Base()); s_TooltipWindow->SetEditable(false); @@ -282,7 +282,7 @@ TextTooltip::~TextTooltip() void TextTooltip::SetText(const char *text) { BaseTooltip::SetText( text ); - + if (s_TooltipWindow.Get()) { s_TooltipWindow->SetText(m_Text.Base()); @@ -378,17 +378,17 @@ void TextTooltip::SizeTextWindow() s_TooltipWindow->SetMultiline(true); s_TooltipWindow->SetSize((int)newWide, (int)newTall ); s_TooltipWindow->SetToFullHeight(); - + s_TooltipWindow->GetSize( wide, tall ); - - if (( wide < 100 ) && ( s_TooltipWindow->GetNumLines() == 2) ) + + if (( wide < 100 ) && ( s_TooltipWindow->GetNumLines() == 2) ) { s_TooltipWindow->SetMultiline(false); - s_TooltipWindow->SetToFullWidth(); + s_TooltipWindow->SetToFullWidth(); } else { - + while ( (float)wide/(float)tall < 2 ) { s_TooltipWindow->SetSize( wide + 1, tall ); @@ -397,7 +397,7 @@ void TextTooltip::SizeTextWindow() } } s_TooltipWindow->GetSize( wide, tall ); - // ivgui()->DPrintf("End Ratio: %f\n", (float)wide/(float)tall); + // ivgui()->DPrintf("End Ratio: %f\n", (float)wide/(float)tall); } } diff --git a/vgui2/vgui_controls/TreeView.cpp b/vgui2/vgui_controls/TreeView.cpp index b7ad4d3b8..c244edebd 100644 --- a/vgui2/vgui_controls/TreeView.cpp +++ b/vgui2/vgui_controls/TreeView.cpp @@ -418,7 +418,7 @@ class TreeNode : public Panel public: TreeNode(Panel *parent, TreeView *pTreeView); - ~TreeNode(); + virtual ~TreeNode(); void SetText(const char *pszText); void SetFont(HFont font); void SetKeyValues(KeyValues *data); diff --git a/vpc_scripts/groups.vgc b/vpc_scripts/groups.vgc index e816e3179..18b59a1c2 100644 --- a/vpc_scripts/groups.vgc +++ b/vpc_scripts/groups.vgc @@ -40,6 +40,7 @@ $Group "shaders" "stdshader_dx7" "stdshader_dx8" "stdshader_dx9" + "mathlib" } $Group "ShaderCompile" @@ -191,6 +192,7 @@ $Group "game" "vphysics" "video_services" "vpklib" + "vscript" "vstdlib" "vtf" "coroutine_osx" @@ -198,6 +200,11 @@ $Group "game" // "zlib" } +$Group "shaderdlls" +{ + "game_shader_dx9 +} + $Group "everything" { "vaudio_minimp3" @@ -313,6 +320,7 @@ $Group "everything" "remoteshadercompile" "replay" "replay_common" + "responsrules" "rt_test" "sampletool" "scenefilecache" @@ -410,6 +418,7 @@ $Group "everything" "vrad_dll" "vrad_launcher" "vsblendeditor_maya2009" + "vscript" "vsdmxio_maya2009" "vsdmxio_maya2010" "vsdmxio_maya2011" @@ -503,6 +512,20 @@ $Group "dedicated" "vtf" } +$Group "maptools" +{ + "vbsp" + "vrad_dll" + "vrad_launcher" + "vvis_dll" + "vvis_launcher" + "fgdlib" + "mathlib" + "raytrace" + "tier1" + "vscript" +} + // A dummy group so that we can include the dedicated project by itself. // You can't do this with +dedicated, because there's a group named dedicated $Group "dedicated_dll" diff --git a/vpc_scripts/projects.vgc b/vpc_scripts/projects.vgc index eca0df679..ea1f2e9b9 100644 --- a/vpc_scripts/projects.vgc +++ b/vpc_scripts/projects.vgc @@ -599,6 +599,11 @@ $Project "mathlib" "mathlib\mathlib.vpc" [$WINDOWS||$X360||$POSIX] } +$Project "responserules" +{ + "responserules\runtime\response_rules.vpc" [($WINDOWS||$X360||$POSIX) && $NEW_RESPONSE_SYSTEM] +} + $Project "matsys_controls" { "vgui2\matsys_controls\matsys_controls.vpc" [$WINDOWS||$X360||$POSIX] @@ -1426,6 +1431,11 @@ $Project "vstdlib" "vstdlib\vstdlib.vpc" [$WINDOWS || $X360||$POSIX] } +$Project "vscript" +{ + "vscript\vscript.vpc" +} + $Project "vtaexp" { "utils\vtaexp\vtaexp.vpc" [$WIN32] diff --git a/vpc_scripts/source_base.vpc b/vpc_scripts/source_base.vpc index 17de60eb3..5d5a7650c 100644 --- a/vpc_scripts/source_base.vpc +++ b/vpc_scripts/source_base.vpc @@ -15,6 +15,15 @@ // rel/tf_beta branch: //$Conditional TF_BETA "1" +//----------------------------------------------------------------------------- + +// Mapbase functionality conditionals +$Conditional MAPBASE "1" // Equivalent to (and required for) our MAPBASE preprocessor defined below +$Conditional MAPBASE_VSCRIPT "1" // Toggles VScript implementation (note: interfaces still exist, just the provided implementation is not present) +$Conditional NEW_RESPONSE_SYSTEM "1" // Toggles the new Response System library based on the Alien Swarm SDK + +//----------------------------------------------------------------------------- + $Configuration "Debug" { $Compiler @@ -27,6 +36,9 @@ $Configuration "Debug" // Need to revisit the code to make things run with the _RETAIL preprocessor definition // This line was added in the previous check-in, but had previously not been defined in this branch // $PreprocessorDefinitions "$BASE;_RETAIL" [$RETAIL] + + // Mapbase base definitions + $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] } } @@ -42,5 +54,8 @@ $Configuration "Release" // Need to revisit the code to make things run with the _RETAIL preprocessor definition // This line was added in the previous check-in, but had previously not been defined in this branch // $PreprocessorDefinitions "$BASE;_RETAIL" [$RETAIL] + + // Mapbase base definitions + $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] } } diff --git a/vpc_scripts/source_win32_base.vpc b/vpc_scripts/source_win32_base.vpc index d08fca76b..c758f48ff 100644 --- a/vpc_scripts/source_win32_base.vpc +++ b/vpc_scripts/source_win32_base.vpc @@ -3,7 +3,8 @@ // builds the analyze.vpc file will not be listed as a dependency. $Include "$SRCDIR\vpc_scripts\source_win32_analyze.vpc" [$ANALYZE] -$Conditional $AT_LEAST_VS2015 "true" [$VS2015] +$Conditional $AT_LEAST_VS2017 "true" [$VS2017 || $VS2019 || $VS2022] +$Conditional $AT_LEAST_VS2015 "true" [$VS2015 || $AT_LEAST_VS2017] $Conditional $AT_LEAST_VS2013 "true" [$VS2013 || $AT_LEAST_VS2015] $Configuration @@ -14,11 +15,14 @@ $Configuration $PlatformToolset "v100" [$VS2010] $PlatformToolset "v110_xp" [$VS2012 && !$ANALYZE] // VS 2012 targeting Windows XP - http://msdn.microsoft.com/en-us/library/vstudio/jj851139.aspx $PlatformToolset "v110" [$VS2012 && $ANALYZE] // VS 2012 for /analyze - $PlatformToolset "v120_xp" [$VS2013 && !$ANALYZE] // VS 2013 targeting Windows XP - http://msdn.microsoft.com/en-us/library/vstudio/jj851139.aspx + $PlatformToolset "v120_xp" [$VS2013 && !$ANALYZE] // VS 2013 targeting Windows XP $PlatformToolset "v120" [$VS2013 && $ANALYZE] // VS 2013 for /analyze - $PlatformToolset "v140_xp" [$VS2015 && !$ANALYZE] // VS 2015 for /analyze + $PlatformToolset "v140_xp" [$VS2015 && !$ANALYZE] // VS 2015 targeting Windows XP $PlatformToolset "v140" [$VS2015 && $ANALYZE] // VS 2015 for /analyze - $PlatformToolset "v142" [$VS2019] // VS 2019 + $PlatformToolset "v141_xp" [($AT_LEAST_VS2017) && !$ANALYZE] // VS 2017+ targeting Windows XP + $PlatformToolset "v141" [$VS2017] // VS 2017 for /analyze + $PlatformToolset "v142" [$VS2019] // VS 2019 for /analyze + $PlatformToolset "v143" [$VS2022] // VS 2022 for /analyze } $General @@ -42,6 +46,10 @@ $Configuration // following are needed for VS2015 using old Windows SDK. Would like to move to each include location $DisableSpecificWarnings "$BASE;4577;4091" [$WINDOWS && $AT_LEAST_VS2015] + // warning C4838: conversion requires a narrowing conversion + // warning C4456-4459: variable shadowing. TODO: fix those! + $DisableSpecificWarnings "$BASE;4316;4838;4456;4457;4458;4459" [($AT_LEAST_VS2015)] + // Having lots of warnings makes it harder to notice new, and possibly // important warnings, both on buildbot and in the output window. Lots @@ -67,7 +75,7 @@ $Configuration $AdditionalOptions "$BASE /Zc:threadSafeInit-" [$AT_LEAST_VS2015] //thread-safe static initialization will crash on Windows XP if the dll is dynamically loaded - $AdditionalOptions "$BASE /Wv:18" [$AT_LEAST_VS2015&&!$WARN_ALL] //warning C4456: declaration of 'accFactorZ' hides previous local declaration suppression + $AdditionalOptions "$BASE /Wv:18" [$AT_LEAST_VS2015 && !$WARN_ALL] //warning C4456: declaration of 'accFactorZ' hides previous local declaration suppression } } diff --git a/vscript/sqstdtime.h b/vscript/sqstdtime.h new file mode 100644 index 000000000..fbf1bee72 --- /dev/null +++ b/vscript/sqstdtime.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------- +// see copyright notice in squirrel.h +// +// Purpose : Squirrel time library cropped out from +// the system library as a safe include. +// +//----------------------------------------------------------------------- + +#include "squirrel.h" +#include "time.h" + +static SQInteger _system_clock(HSQUIRRELVM v) +{ + sq_pushfloat(v, ((SQFloat)clock()) / (SQFloat)CLOCKS_PER_SEC); + return 1; +} + +static SQInteger _system_time(HSQUIRRELVM v) +{ + SQInteger t = (SQInteger)time(NULL); + sq_pushinteger(v, t); + return 1; +} + +static void _set_integer_slot(HSQUIRRELVM v, const SQChar *name, SQInteger val) +{ + sq_pushstring(v, name, -1); + sq_pushinteger(v, val); + sq_rawset(v, -3); +} + +static SQInteger _system_date(HSQUIRRELVM v) +{ + time_t t; + SQInteger it; + SQInteger format = 'l'; + if (sq_gettop(v) > 1) { + sq_getinteger(v, 2, &it); + t = it; + if (sq_gettop(v) > 2) { + sq_getinteger(v, 3, (SQInteger*)&format); + } + } + else { + time(&t); + } + tm *date; + if (format == 'u') + date = gmtime(&t); + else + date = localtime(&t); + if (!date) + return sq_throwerror(v, _SC("crt api failure")); + sq_newtable(v); + _set_integer_slot(v, _SC("sec"), date->tm_sec); + _set_integer_slot(v, _SC("min"), date->tm_min); + _set_integer_slot(v, _SC("hour"), date->tm_hour); + _set_integer_slot(v, _SC("day"), date->tm_mday); + _set_integer_slot(v, _SC("month"), date->tm_mon); + _set_integer_slot(v, _SC("year"), date->tm_year + 1900); + _set_integer_slot(v, _SC("wday"), date->tm_wday); + _set_integer_slot(v, _SC("yday"), date->tm_yday); + return 1; +} + +#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask} +static const SQRegFunction timelib_funcs[] = { + _DECL_FUNC(clock, 0, NULL), + _DECL_FUNC(time, 1, NULL), + _DECL_FUNC(date, -1, _SC(".nn")), + { NULL, (SQFUNCTION)0, 0, NULL } +}; +#undef _DECL_FUNC + +SQInteger sqstd_register_timelib(HSQUIRRELVM v) +{ + SQInteger i = 0; + while (timelib_funcs[i].name != 0) + { + sq_pushstring(v, timelib_funcs[i].name, -1); + sq_newclosure(v, timelib_funcs[i].f, 0); + sq_setparamscheck(v, timelib_funcs[i].nparamscheck, timelib_funcs[i].typemask); + sq_setnativeclosurename(v, -1, timelib_funcs[i].name); + sq_newslot(v, -3, SQFalse); + i++; + } + return 1; +} diff --git a/vscript/vscript.cpp b/vscript/vscript.cpp new file mode 100644 index 000000000..af1570adf --- /dev/null +++ b/vscript/vscript.cpp @@ -0,0 +1,82 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom implementation of VScript in Source 2013, created from scratch +// using the Alien Swarm SDK as a reference for Valve's library. +// +// Author(s): ReDucTor (header written by Blixibon) +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" + +#include "vscript_bindings_base.h" + +#include "tier1/tier1.h" + +IScriptVM* makeSquirrelVM(); + +int vscript_token = 0; + +class CScriptManager : public CTier1AppSystem +{ +public: + virtual IScriptVM* CreateVM(ScriptLanguage_t language) override + { + IScriptVM* pScriptVM = nullptr; + if (language == SL_SQUIRREL) + { + pScriptVM = makeSquirrelVM(); + } + else + { + return nullptr; + } + + if (pScriptVM == nullptr) + { + return nullptr; + } + + if (!pScriptVM->Init()) + { + delete pScriptVM; + return nullptr; + } + + // Register base bindings for all VMs + RegisterBaseBindings( pScriptVM ); + + return pScriptVM; + } + + virtual void DestroyVM(IScriptVM * pScriptVM) override + { + if (pScriptVM) + { + pScriptVM->Shutdown(); + delete pScriptVM; + } + } + + // Mapbase moves CScriptKeyValues into the library so it could be used elsewhere + virtual HSCRIPT CreateScriptKeyValues( IScriptVM *pVM, KeyValues *pKV, bool bAllowDestruct ) override + { + CScriptKeyValues *pSKV = new CScriptKeyValues( pKV ); + HSCRIPT hSKV = pVM->RegisterInstance( pSKV, bAllowDestruct ); + return hSKV; + } + + virtual KeyValues *GetKeyValuesFromScriptKV( IScriptVM *pVM, HSCRIPT hSKV ) override + { + CScriptKeyValues *pSKV = (hSKV ? (CScriptKeyValues*)pVM->GetInstanceValue( hSKV, GetScriptDesc( (CScriptKeyValues*)NULL ) ) : nullptr); + if (pSKV) + { + return pSKV->m_pKeyValues; + } + + return nullptr; + } +}; + +EXPOSE_SINGLE_INTERFACE(CScriptManager, IScriptManager, VSCRIPT_INTERFACE_VERSION); \ No newline at end of file diff --git a/vscript/vscript.vpc b/vscript/vscript.vpc new file mode 100644 index 000000000..22b6bcc70 --- /dev/null +++ b/vscript/vscript.vpc @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// VSCRIPT.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR ".." + +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;.\squirrel\include" + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] + } +} + +$Project "VScript" +{ + $Folder "Source Files" + { + + $File "vscript.cpp" + $File "vscript_squirrel.cpp" + + $File "vscript_bindings_base.cpp" + $File "vscript_bindings_base.h" + $File "vscript_bindings_math.cpp" + $File "vscript_bindings_math.h" + + $Folder "squirrel" + { + $File ".\squirrel\sqstdlib\sqstdaux.cpp" \ + ".\squirrel\sqstdlib\sqstdblob.cpp" \ + ".\squirrel\sqstdlib\sqstdio.cpp" \ + ".\squirrel\sqstdlib\sqstdmath.cpp" \ + ".\squirrel\sqstdlib\sqstdrex.cpp" \ + ".\squirrel\sqstdlib\sqstdstream.cpp" \ + ".\squirrel\sqstdlib\sqstdstring.cpp" \ + ".\squirrel\sqstdlib\sqstdsystem.cpp" \ + ".\squirrel\squirrel\sqapi.cpp" \ + ".\squirrel\squirrel\sqbaselib.cpp" \ + ".\squirrel\squirrel\sqclass.cpp" \ + ".\squirrel\squirrel\sqcompiler.cpp" \ + ".\squirrel\squirrel\sqdebug.cpp" \ + ".\squirrel\squirrel\sqfuncstate.cpp" \ + ".\squirrel\squirrel\sqlexer.cpp" \ + ".\squirrel\squirrel\sqmem.cpp" \ + ".\squirrel\squirrel\sqobject.cpp" \ + ".\squirrel\squirrel\sqstate.cpp" \ + ".\squirrel\squirrel\sqtable.cpp" \ + ".\squirrel\squirrel\sqvm.cpp" + { + $Configuration + { + $Compiler + { + // Squirrel third party library is full of warnings + $AdditionalOptions "$BASE /wd4100 /wd4611 /wd4127 /wd4244 /wd4702 /wd4706 /wd4800" + $TreatWarningsAsErrors "No" + } + } + } + } + } +} diff --git a/vscript/vscript_bindings_base.cpp b/vscript/vscript_bindings_base.cpp new file mode 100644 index 000000000..4f20234a6 --- /dev/null +++ b/vscript/vscript_bindings_base.cpp @@ -0,0 +1,563 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript functions, constants, etc. registered within the library itself. +// +// This is for things which don't have to depend on server/client and can be accessed +// from anywhere. +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" + +#include "tier1/tier1.h" +#include "tier1/fmtstr.h" + +#include +#include "icommandline.h" +#include "worldsize.h" +#include "bspflags.h" + +#include + +#include "vscript_bindings_base.h" +#include "vscript_bindings_math.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IScriptManager *scriptmanager; + +//============================================================================= +// +// Prints +// +//============================================================================= +static void ScriptMsg( const char *msg ) +{ + Msg( "%s", msg ); +} + +static void ScriptColorPrint( int r, int g, int b, const char *pszMsg ) +{ + const Color clr(r, g, b, 255); + ConColorMsg( clr, "%s", pszMsg ); +} + +static void ScriptColorPrintL( int r, int g, int b, const char *pszMsg ) +{ + const Color clr(r, g, b, 255); + ConColorMsg( clr, "%s\n", pszMsg ); +} + + +//============================================================================= +// +// Command Line +// +//============================================================================= +class CGlobalSys +{ +public: + const char* ScriptGetCommandLine() + { + return CommandLine()->GetCmdLine(); + } + + bool CommandLineCheck(const char* name) + { + return !!CommandLine()->FindParm(name); + } + + const char* CommandLineCheckStr(const char* name) + { + return CommandLine()->ParmValue(name); + } + + float CommandLineCheckFloat(const char* name) + { + return CommandLine()->ParmValue(name, 0); + } + + int CommandLineCheckInt(const char* name) + { + return CommandLine()->ParmValue(name, 0); + } +} g_ScriptGlobalSys; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CGlobalSys, "CGlobalSys", SCRIPT_SINGLETON "GlobalSys" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetCommandLine, "GetCommandLine", "returns the command line" ) + DEFINE_SCRIPTFUNC( CommandLineCheck, "returns true if the command line param was used, otherwise false." ) + DEFINE_SCRIPTFUNC( CommandLineCheckStr, "returns the command line param as a string." ) + DEFINE_SCRIPTFUNC( CommandLineCheckFloat, "returns the command line param as a float." ) + DEFINE_SCRIPTFUNC( CommandLineCheckInt, "returns the command line param as an int." ) +END_SCRIPTDESC(); + +// ---------------------------------------------------------------------------- +// KeyValues access - CBaseEntity::ScriptGetKeyFromModel returns root KeyValues +// ---------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC_NAMED( ScriptFindKey, "FindKey", "Given a KeyValues object and a key name, find a KeyValues object associated with the key name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstSubKey, "GetFirstSubKey", "Given a KeyValues object, return the first sub key object" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNextKey, "GetNextKey", "Given a KeyValues object, return the next key object in a sub key group" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueInt, "GetKeyInt", "Given a KeyValues object and a key name, return associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueFloat, "GetKeyFloat", "Given a KeyValues object and a key name, return associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBool, "GetKeyBool", "Given a KeyValues object and a key name, return associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueString, "GetKeyString", "Given a KeyValues object and a key name, return associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptIsKeyValueEmpty, "IsKeyEmpty", "Given a KeyValues object and a key name, return true if key name has no value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); + + 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" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetName, "GetName", "Given a KeyValues object, return its name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetInt, "GetInt", "Given a KeyValues object, return its own associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFloat, "GetFloat", "Given a KeyValues object, return its own associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetString, "GetString", "Given a KeyValues object, return its own associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBool, "GetBool", "Given a KeyValues object, return its own associated bool value" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueInt, "SetKeyInt", "Given a KeyValues object and a key name, set associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueFloat, "SetKeyFloat", "Given a KeyValues object and a key name, set associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueBool, "SetKeyBool", "Given a KeyValues object and a key name, set associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueString, "SetKeyString", "Given a KeyValues object and a key name, set associated string value" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetName, "SetName", "Given a KeyValues object, set its name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetInt, "SetInt", "Given a KeyValues object, set its own associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFloat, "SetFloat", "Given a KeyValues object, set its own associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetBool, "SetBool", "Given a KeyValues object, set its own associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetString, "SetString", "Given a KeyValues object, set its own associated string value" ); +END_SCRIPTDESC(); + +HSCRIPT CScriptKeyValues::ScriptFindKey( const char *pszName ) +{ + KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetFirstSubKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetFirstSubKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetNextKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetNextKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +int CScriptKeyValues::ScriptGetKeyValueInt( const char *pszName ) +{ + int i = m_pKeyValues->GetInt( pszName ); + return i; +} + +float CScriptKeyValues::ScriptGetKeyValueFloat( const char *pszName ) +{ + float f = m_pKeyValues->GetFloat( pszName ); + return f; +} + +const char *CScriptKeyValues::ScriptGetKeyValueString( const char *pszName ) +{ + const char *psz = m_pKeyValues->GetString( pszName ); + return psz; +} + +bool CScriptKeyValues::ScriptIsKeyValueEmpty( const char *pszName ) +{ + bool b = m_pKeyValues->IsEmpty( pszName ); + return b; +} + +bool CScriptKeyValues::ScriptGetKeyValueBool( const char *pszName ) +{ + bool b = m_pKeyValues->GetBool( pszName ); + return b; +} + +void CScriptKeyValues::ScriptReleaseKeyValues( ) +{ + m_pKeyValues->deleteThis(); + m_pKeyValues = NULL; +} + +void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) +{ + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &varKey, &varValue )) != -1) + { + switch (varValue.m_type) + { + case FIELD_CSTRING: m_pKeyValues->SetString( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_INTEGER: m_pKeyValues->SetInt( varKey.m_pszString, varValue.m_int ); break; + case FIELD_FLOAT: m_pKeyValues->SetFloat( varKey.m_pszString, varValue.m_float ); break; + case FIELD_BOOLEAN: m_pKeyValues->SetBool( varKey.m_pszString, varValue.m_bool ); break; + case FIELD_VECTOR: m_pKeyValues->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break; + } + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } +} + +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); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +const char *CScriptKeyValues::ScriptGetName() +{ + const char *psz = m_pKeyValues->GetName(); + return psz; +} + +int CScriptKeyValues::ScriptGetInt() +{ + int i = m_pKeyValues->GetInt(); + return i; +} + +float CScriptKeyValues::ScriptGetFloat() +{ + float f = m_pKeyValues->GetFloat(); + return f; +} + +const char *CScriptKeyValues::ScriptGetString() +{ + const char *psz = m_pKeyValues->GetString(); + return psz; +} + +bool CScriptKeyValues::ScriptGetBool() +{ + bool b = m_pKeyValues->GetBool(); + return b; +} + + +void CScriptKeyValues::ScriptSetKeyValueInt( const char *pszName, int iValue ) +{ + m_pKeyValues->SetInt( pszName, iValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueFloat( const char *pszName, float flValue ) +{ + m_pKeyValues->SetFloat( pszName, flValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueString( const char *pszName, const char *pszValue ) +{ + m_pKeyValues->SetString( pszName, pszValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueBool( const char *pszName, bool bValue ) +{ + m_pKeyValues->SetBool( pszName, bValue ); +} + +void CScriptKeyValues::ScriptSetName( const char *pszValue ) +{ + m_pKeyValues->SetName( pszValue ); +} + +void CScriptKeyValues::ScriptSetInt( int iValue ) +{ + m_pKeyValues->SetInt( NULL, iValue ); +} + +void CScriptKeyValues::ScriptSetFloat( float flValue ) +{ + m_pKeyValues->SetFloat( NULL, flValue ); +} + +void CScriptKeyValues::ScriptSetString( const char *pszValue ) +{ + m_pKeyValues->SetString( NULL, pszValue ); +} + +void CScriptKeyValues::ScriptSetBool( bool bValue ) +{ + m_pKeyValues->SetBool( NULL, bValue ); +} + + +// constructors +CScriptKeyValues::CScriptKeyValues( KeyValues *pKeyValues = NULL ) +{ + if (pKeyValues == NULL) + { + m_pKeyValues = new KeyValues("CScriptKeyValues"); + } + else + { + m_pKeyValues = pKeyValues; + } +} + +// destructor +CScriptKeyValues::~CScriptKeyValues( ) +{ + if (m_pKeyValues) + { + m_pKeyValues->deleteThis(); + } + m_pKeyValues = NULL; +} + +//============================================================================= +// +// matrix3x4_t +// +//============================================================================= +CScriptColorInstanceHelper g_ColorScriptInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT( Color, "" ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPT_INSTANCE_HELPER( &g_ColorScriptInstanceHelper ) + + DEFINE_SCRIPTFUNC( SetColor, "Sets the color." ) + + DEFINE_SCRIPTFUNC( SetRawColor, "Sets the raw color integer." ) + DEFINE_SCRIPTFUNC( GetRawColor, "Gets the raw color integer." ) + + DEFINE_MEMBERVAR( "r", FIELD_CHARACTER, "Member variable for red." ) + DEFINE_MEMBERVAR( "g", FIELD_CHARACTER, "Member variable for green." ) + DEFINE_MEMBERVAR( "b", FIELD_CHARACTER, "Member variable for blue." ) + DEFINE_MEMBERVAR( "a", FIELD_CHARACTER, "Member variable for alpha. (transparency)" ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- + +bool CScriptColorInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + Color *pClr = ((Color *)p); + V_snprintf( pBuf, bufSize, "(color: (%i, %i, %i, %i))", pClr->r(), pClr->g(), pClr->b(), pClr->a() ); + return true; +} + +bool CScriptColorInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Color *pClr = ((Color *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'r': + variant = pClr->r(); + return true; + case 'g': + variant = pClr->g(); + return true; + case 'b': + variant = pClr->b(); + return true; + case 'a': + variant = pClr->a(); + return true; + } + } + return false; +} + +bool CScriptColorInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Color *pClr = ((Color *)p); + if ( strlen(pszKey) == 1 ) + { + int iVal; + variant.AssignTo( &iVal ); + switch (pszKey[0]) + { + // variant.AssignTo( &(*pClr)[0] ); + case 'r': + (*pClr)[0] = iVal; + return true; + case 'g': + (*pClr)[1] = iVal; + return true; + case 'b': + (*pClr)[2] = iVal; + return true; + case 'a': + (*pClr)[3] = iVal; + return true; + } + } + return false; +} + +//============================================================================= +//============================================================================= + +void RegisterBaseBindings( IScriptVM *pVM ) +{ + ScriptRegisterFunctionNamed( pVM, ScriptMsg, "Msg", "" ); + ScriptRegisterFunctionNamed( pVM, ScriptColorPrint, "printc", "Version of print() which takes a color before the message." ); + ScriptRegisterFunctionNamed( pVM, ScriptColorPrintL, "printcl", "Version of printl() which takes a color before the message." ); + + ScriptRegisterFunction( pVM, GetCPUUsage, "Get CPU usage percentage." ); + + //----------------------------------------------------------------------------- + + pVM->RegisterInstance( &g_ScriptGlobalSys, "GlobalSys" ); + + //----------------------------------------------------------------------------- + + pVM->RegisterClass( GetScriptDescForClass( CScriptKeyValues ) ); + + pVM->RegisterClass( GetScriptDescForClass( Color ) ); + + //----------------------------------------------------------------------------- + + ScriptRegisterConstant( pVM, MAPBASE_VERSION, "The current Mapbase version according to when the VScript library was last compiled." ); + ScriptRegisterConstant( pVM, MAPBASE_VER_INT, "The current Mapbase version integer according to when the VScript library was last compiled." ); + + // + // Math/world + // + ScriptRegisterConstant( pVM, MAX_COORD_FLOAT, "Maximum float coordinate." ); + ScriptRegisterConstant( pVM, MAX_TRACE_LENGTH, "Maximum traceable distance (assumes cubic world and trace from one corner to opposite)." ); + + // + // Trace Contents/Masks + // + ScriptRegisterConstant( pVM, CONTENTS_EMPTY, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_SOLID, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_WINDOW, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_AUX, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_GRATE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_SLIME, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_WATER, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_BLOCKLOS, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_OPAQUE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TESTFOGVOLUME, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TEAM1, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TEAM2, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_IGNORE_NODRAW_OPAQUE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_MOVEABLE, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_AREAPORTAL, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_PLAYERCLIP, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_MONSTERCLIP, "Spatial content flags." ); + + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_0, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_90, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_180, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_270, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_UP, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_CURRENT_DOWN, "Spatial content flags." ); + + ScriptRegisterConstant( pVM, CONTENTS_ORIGIN, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_MONSTER, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_DEBRIS, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_DETAIL, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_TRANSLUCENT, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_LADDER, "Spatial content flags." ); + ScriptRegisterConstant( pVM, CONTENTS_HITBOX, "Spatial content flags." ); + + ScriptRegisterConstant( pVM, LAST_VISIBLE_CONTENTS, "Contains last visible spatial content flags." ); + ScriptRegisterConstant( pVM, ALL_VISIBLE_CONTENTS, "Contains all visible spatial content flags." ); + + ScriptRegisterConstant( pVM, MASK_SOLID, "Spatial content mask representing solid objects (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_PLAYERSOLID, "Spatial content mask representing objects solid to the player, including player clips (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_NPCSOLID, "Spatial content mask representing objects solid to NPCs, including NPC clips (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_WATER, "Spatial content mask representing water and slime solids (CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_SLIME)" ); + ScriptRegisterConstant( pVM, MASK_OPAQUE, "Spatial content mask representing objects which block lighting (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_OPAQUE)" ); + ScriptRegisterConstant( pVM, MASK_OPAQUE_AND_NPCS, "Spatial content mask equivalent to MASK_OPAQUE, but also including NPCs (MASK_OPAQUE|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( pVM, MASK_BLOCKLOS, "Spatial content mask representing objects which block LOS for AI (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_BLOCKLOS)" ); + ScriptRegisterConstant( pVM, MASK_BLOCKLOS_AND_NPCS, "Spatial content mask equivalent to MASK_BLOCKLOS, but also including NPCs (MASK_BLOCKLOS|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( pVM, MASK_VISIBLE, "Spatial content mask representing objects which block LOS for players (MASK_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE)" ); + ScriptRegisterConstant( pVM, MASK_VISIBLE_AND_NPCS, "Spatial content mask equivalent to MASK_VISIBLE, but also including NPCs (MASK_OPAQUE_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE)" ); + ScriptRegisterConstant( pVM, MASK_SHOT, "Spatial content mask representing objects solid to bullets (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_HITBOX)" ); + ScriptRegisterConstant( pVM, MASK_SHOT_HULL, "Spatial content mask representing objects solid to non-raycasted weapons, including grates (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_SHOT_PORTAL, "Spatial content mask equivalent to MASK_SHOT, but excluding debris and not using expensive hitbox calculations (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER)" ); + ScriptRegisterConstant( pVM, MASK_SOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_SOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_PLAYERSOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_PLAYERSOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_PLAYERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_NPCSOLID_BRUSHONLY, "Spatial content mask equivalent to MASK_NPCSOLID, but without NPCs (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_NPCWORLDSTATIC, "Spatial content mask representing objects static to NPCs, used for nodegraph rebuilding (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE)" ); + ScriptRegisterConstant( pVM, MASK_SPLITAREAPORTAL, "Spatial content mask representing objects which can split areaportals (CONTENTS_WATER|CONTENTS_SLIME)" ); + + // + // Misc. General + // + ScriptRegisterConstant( pVM, FCVAR_NONE, "Empty convar flag." ); + ScriptRegisterConstant( pVM, FCVAR_UNREGISTERED, "If this convar flag is set, it isn't added to linked list, etc." ); + ScriptRegisterConstant( pVM, FCVAR_DEVELOPMENTONLY, "If this convar flag is set, it's hidden in \"retail\" DLLs." ); + ScriptRegisterConstant( pVM, FCVAR_GAMEDLL, "This convar flag is defined in server DLL convars." ); + ScriptRegisterConstant( pVM, FCVAR_CLIENTDLL, "This convar flag is defined in client DLL convars." ); + ScriptRegisterConstant( pVM, FCVAR_HIDDEN, "If this convar flag is set, it doesn't appear in the console or any searching tools, but it can still be set." ); + ScriptRegisterConstant( pVM, FCVAR_PROTECTED, "This convar flag prevents convars with secure data (e.g. passwords) from sending full data to clients, only sending 1 if non-zero and 0 otherwise." ); + ScriptRegisterConstant( pVM, FCVAR_SPONLY, "If this convar flag is set, it can't be changed by clients connected to a multiplayer server." ); + ScriptRegisterConstant( pVM, FCVAR_ARCHIVE, "If this convar flag is set, its value will be saved when the game is exited." ); + ScriptRegisterConstant( pVM, FCVAR_NOTIFY, "If this convar flag is set, it will notify players when it is changed." ); + ScriptRegisterConstant( pVM, FCVAR_CHEAT, "Only useable in singleplayer / debug / multiplayer & sv_cheats" ); + ScriptRegisterConstant( pVM, FCVAR_USERINFO, "If this convar flag is set, it will be marked as info which plays a part in how the server identifies a client." ); + ScriptRegisterConstant( pVM, FCVAR_PRINTABLEONLY, "If this convar flag is set, it cannot contain unprintable characters. Used for player name cvars, etc." ); + ScriptRegisterConstant( pVM, FCVAR_UNLOGGED, "If this convar flag is set, it will not log its changes if a log is being created." ); + ScriptRegisterConstant( pVM, FCVAR_NEVER_AS_STRING, "If this convar flag is set, it will never be printed as a string." ); + ScriptRegisterConstant( pVM, FCVAR_REPLICATED, "If this convar flag is set, it will enforce a serverside value on any clientside counterparts. (also known as FCVAR_SERVER)" ); + ScriptRegisterConstant( pVM, FCVAR_DEMO, "If this convar flag is set, it will be recorded when starting a demo file." ); + ScriptRegisterConstant( pVM, FCVAR_DONTRECORD, "If this convar flag is set, it will NOT be recorded when starting a demo file." ); + ScriptRegisterConstant( pVM, FCVAR_RELOAD_MATERIALS, "If this convar flag is set, it will force a material reload when it changes." ); + ScriptRegisterConstant( pVM, FCVAR_RELOAD_TEXTURES, "If this convar flag is set, it will force a texture reload when it changes." ); + ScriptRegisterConstant( pVM, FCVAR_NOT_CONNECTED, "If this convar flag is set, it cannot be changed by a client connected to the server." ); + ScriptRegisterConstant( pVM, FCVAR_MATERIAL_SYSTEM_THREAD, "This convar flag indicates it's read from the material system thread." ); + ScriptRegisterConstant( pVM, FCVAR_ARCHIVE_XBOX, "If this convar flag is set, it will be archived on the Xbox config." ); + ScriptRegisterConstant( pVM, FCVAR_ACCESSIBLE_FROM_THREADS, "If this convar flag is set, it will be accessible from the material system thread." ); + ScriptRegisterConstant( pVM, FCVAR_SERVER_CAN_EXECUTE, "If this convar flag is set, the server will be allowed to execute it as a client command." ); + ScriptRegisterConstant( pVM, FCVAR_SERVER_CANNOT_QUERY, "If this convar flag is set, the server will not be allowed to query its value." ); + ScriptRegisterConstant( pVM, FCVAR_CLIENTCMD_CAN_EXECUTE, "If this convar flag is set, any client will be allowed to execute this command." ); + + //----------------------------------------------------------------------------- + + RegisterMathBaseBindings( pVM ); +} diff --git a/vscript/vscript_bindings_base.h b/vscript/vscript_bindings_base.h new file mode 100644 index 000000000..2629aada4 --- /dev/null +++ b/vscript/vscript_bindings_base.h @@ -0,0 +1,76 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_BINDINGS_BASE +#define VSCRIPT_BINDINGS_BASE +#ifdef _WIN32 +#pragma once +#endif + +#include "vscript/ivscript.h" +#include "tier1/KeyValues.h" + +// ---------------------------------------------------------------------------- +// KeyValues access +// ---------------------------------------------------------------------------- +class CScriptKeyValues +{ +public: + CScriptKeyValues( KeyValues *pKeyValues ); + ~CScriptKeyValues( ); + + HSCRIPT ScriptFindKey( const char *pszName ); + HSCRIPT ScriptGetFirstSubKey( void ); + HSCRIPT ScriptGetNextKey( void ); + int ScriptGetKeyValueInt( const char *pszName ); + float ScriptGetKeyValueFloat( const char *pszName ); + const char *ScriptGetKeyValueString( const char *pszName ); + bool ScriptIsKeyValueEmpty( const char *pszName ); + bool ScriptGetKeyValueBool( const char *pszName ); + void ScriptReleaseKeyValues( ); + + // Functions below are new with Mapbase + void TableToSubKeys( HSCRIPT hTable ); + void SubKeysToTable( HSCRIPT hTable ); + + HSCRIPT ScriptFindOrCreateKey( const char *pszName ); + + const char *ScriptGetName(); + int ScriptGetInt(); + float ScriptGetFloat(); + const char *ScriptGetString(); + bool ScriptGetBool(); + + void ScriptSetKeyValueInt( const char *pszName, int iValue ); + void ScriptSetKeyValueFloat( const char *pszName, float flValue ); + void ScriptSetKeyValueString( const char *pszName, const char *pszValue ); + void ScriptSetKeyValueBool( const char *pszName, bool bValue ); + void ScriptSetName( const char *pszValue ); + void ScriptSetInt( int iValue ); + void ScriptSetFloat( float flValue ); + void ScriptSetString( const char *pszValue ); + void ScriptSetBool( bool bValue ); + + KeyValues *GetKeyValues() { return m_pKeyValues; } + + KeyValues *m_pKeyValues; // actual KeyValue entity +}; + +//----------------------------------------------------------------------------- +// Exposes Color to VScript +//----------------------------------------------------------------------------- +class CScriptColorInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); +}; + +void RegisterBaseBindings( IScriptVM *pVM ); + +#endif diff --git a/vscript/vscript_bindings_math.cpp b/vscript/vscript_bindings_math.cpp new file mode 100644 index 000000000..cb1567d54 --- /dev/null +++ b/vscript/vscript_bindings_math.cpp @@ -0,0 +1,515 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: VScript functions, constants, etc. registered within the library itself. +// +// This is for things which don't have to depend on server/client and can be accessed +// from anywhere. +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" + +#include "tier1/tier1.h" + +#include +#include "worldsize.h" + +#include + +#include "vscript_bindings_math.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//============================================================================= +// +// matrix3x4_t +// +//============================================================================= +BEGIN_SCRIPTDESC_ROOT_NAMED( matrix3x4_t, "matrix3x4_t", "A 3x4 matrix transform." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC( Init, "Creates a matrix where the X axis = forward, the Y axis = left, and the Z axis = up." ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptConcatTransforms( HSCRIPT hMat1, HSCRIPT hMat2, HSCRIPT hOut ) +{ + if (!hMat1 || !hMat2 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + ConcatTransforms( *pMat1, *pMat2, *pOut ); +} + +void ScriptMatrixCopy( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixCopy( *pMat1, *pOut ); +} + +void ScriptMatrixInvert( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixInvert( *pMat1, *pOut ); +} + +void ScriptMatricesAreEqual( HSCRIPT hMat1, HSCRIPT hMat2 ) +{ + if (!hMat1 || !hMat2) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + + MatricesAreEqual( *pMat1, *pMat2 ); +} + +const Vector& ScriptMatrixGetColumn( HSCRIPT hMat1, int column ) +{ + static Vector outvec; + outvec.Zero(); + if (!hMat1) + return outvec; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixGetColumn( *pMat1, column, outvec ); + return outvec; +} + +void ScriptMatrixSetColumn( const Vector& vecset, int column, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixSetColumn( vecset, column, *pMat1 ); +} + +void ScriptMatrixAngles( HSCRIPT hMat1, const QAngle& angset, const Vector& vecset ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixAngles( *pMat1, *const_cast(&angset), *const_cast(&vecset) ); +} + +void ScriptAngleMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleMatrix( angset, vecset, *pMat1 ); +} + +void ScriptAngleIMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleIMatrix( angset, vecset, *pMat1 ); +} + +void ScriptSetIdentityMatrix( HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetIdentityMatrix( *pMat1 ); +} + +void ScriptSetScaleMatrix( float x, float y, float z, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetScaleMatrix( x, y, z, *pMat1 ); +} + +void ScriptMatrixScaleBy( float flScale, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixScaleBy( flScale, *pMat1 ); +} + +void ScriptMatrixScaleByZero( HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixScaleByZero( *pMat1 ); +} + +const Vector& ScriptMatrixGetTranslation( HSCRIPT hMat1 ) +{ + static Vector outvec; + outvec.Zero(); + if (!hMat1) + return outvec; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixGetTranslation( *pMat1, outvec ); + return outvec; +} + +void ScriptMatrixSetTranslation( const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixSetTranslation( vecset, *pMat1 ); +} + +//============================================================================= +// +// Quaternion +// +//============================================================================= +CScriptQuaternionInstanceHelper g_QuaternionScriptInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT_NAMED( Quaternion, "Quaternion", "A quaternion." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPT_INSTANCE_HELPER( &g_QuaternionScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( ScriptInit, "Init", "Creates a quaternion with the given values." ) + + DEFINE_MEMBERVAR( "x", FIELD_FLOAT, "The quaternion's i axis component." ) + DEFINE_MEMBERVAR( "y", FIELD_FLOAT, "The quaternion's j axis component." ) + DEFINE_MEMBERVAR( "z", FIELD_FLOAT, "The quaternion's k axis component." ) + DEFINE_MEMBERVAR( "w", FIELD_FLOAT, "The quaternion's scalar component." ) + +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- + +bool CScriptQuaternionInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + Quaternion *pQuat = ((Quaternion *)p); + V_snprintf( pBuf, bufSize, "(Quaternion %p [%f %f %f %f])", (void*)pQuat, pQuat->x, pQuat->y, pQuat->z, pQuat->w ); + return true; +} + +bool CScriptQuaternionInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'x': + variant = pQuat->x; + return true; + case 'y': + variant = pQuat->y; + return true; + case 'z': + variant = pQuat->z; + return true; + case 'w': + variant = pQuat->w; + return true; + } + } + return false; +} + +bool CScriptQuaternionInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + if ( strlen(pszKey) == 1 ) + { + switch (pszKey[0]) + { + case 'x': + variant.AssignTo( &pQuat->x ); + return true; + case 'y': + variant.AssignTo( &pQuat->y ); + return true; + case 'z': + variant.AssignTo( &pQuat->z ); + return true; + case 'w': + variant.AssignTo( &pQuat->w ); + return true; + } + } + return false; +} + +ScriptVariant_t *CScriptQuaternionInstanceHelper::Add( void *p, ScriptVariant_t &variant ) +{ + Quaternion *pQuat = ((Quaternion *)p); + + float flAdd; + variant.AssignTo( &flAdd ); + + (*pQuat)[0] += flAdd; + (*pQuat)[1] += flAdd; + (*pQuat)[2] += flAdd; + (*pQuat)[3] += flAdd; + + static ScriptVariant_t result; + result = (HSCRIPT)p; + return &result; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptQuaternionAdd( HSCRIPT hQuat1, HSCRIPT hQuat2, HSCRIPT hOut ) +{ + if (!hQuat1 || !hQuat2 || !hOut) + return; + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + Quaternion *pQuat2 = ToQuaternion( hQuat2 ); + Quaternion *pOut = ToQuaternion( hOut ); + + QuaternionAdd( *pQuat1, *pQuat2, *pOut ); +} + +void ScriptMatrixQuaternion( HSCRIPT hMat1, HSCRIPT hQuat1 ) +{ + if (!hMat1 || !hQuat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + + MatrixQuaternion( *pMat1, *pQuat1 ); +} + +void ScriptQuaternionMatrix( HSCRIPT hQuat1, HSCRIPT hMat1 ) +{ + if (!hQuat1 || !hMat1) + return; + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + QuaternionMatrix( *pQuat1, *pMat1 ); +} + +QAngle ScriptQuaternionAngles( HSCRIPT hQuat1 ) +{ + if (!hQuat1) + return QAngle(); + + Quaternion *pQuat1 = ToQuaternion( hQuat1 ); + + QAngle angles; + QuaternionAngles( *pQuat1, angles ); + return angles; +} + +//============================================================================= +// +// Misc. Vector/QAngle functions +// +//============================================================================= + +const Vector& ScriptAngleVectors( const QAngle &angles ) +{ + static Vector forward; + AngleVectors( angles, &forward ); + return forward; +} + +const QAngle& ScriptVectorAngles( const Vector &forward ) +{ + static QAngle angles; + VectorAngles( forward, angles ); + return angles; +} + +const Vector& ScriptVectorRotate( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorRotate( in, *ToMatrix3x4(hMat), out ); + return out; +} + +const Vector& ScriptVectorIRotate( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorIRotate( in, *ToMatrix3x4(hMat), out ); + return out; +} + +const Vector& ScriptVectorTransform( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorTransform( in, *ToMatrix3x4( hMat ), out ); + return out; +} + +const Vector& ScriptVectorITransform( const Vector &in, HSCRIPT hMat ) +{ + if (ToMatrix3x4(hMat) == NULL) + return vec3_origin; + + static Vector out; + VectorITransform( in, *ToMatrix3x4( hMat ), out ); + return out; +} + +const Vector& ScriptCalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point ) +{ + static Vector outvec; + CalcClosestPointOnAABB( mins, maxs, point, outvec ); + return outvec; +} + +const Vector& ScriptCalcClosestPointOnLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLine( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLine( point, vLineA, vLineB ); +} + +const Vector& ScriptCalcClosestPointOnLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLineSegment( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + 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", "" ); + ScriptRegisterConstantNamed( pVM, ((float)(M_PI_F / 180.f)), "DEG2RAD", "" ); + + ScriptRegisterFunction( pVM, RandomFloat, "Generate a random floating point number within a range, inclusive." ); + ScriptRegisterFunction( pVM, RandomInt, "Generate a random integer within a range, inclusive." ); + //ScriptRegisterFunction( pVM, Approach, "Returns a value which approaches the target value from the input value with the specified speed." ); + ScriptRegisterFunction( pVM, ApproachAngle, "Returns an angle which approaches the target angle from the input angle with the specified speed." ); + ScriptRegisterFunction( pVM, AngleDiff, "Returns the degrees difference between two yaw angles." ); + //ScriptRegisterFunction( pVM, AngleDistance, "Returns the distance between two angles." ); + ScriptRegisterFunction( pVM, AngleNormalize, "Clamps an angle to be in between -360 and 360." ); + ScriptRegisterFunction( pVM, AngleNormalizePositive, "Clamps an angle to be in between 0 and 360." ); + ScriptRegisterFunction( pVM, AnglesAreEqual, "Checks if two angles are equal based on a given tolerance value." ); + + // + // matrix3x4_t + // + pVM->RegisterClass( GetScriptDescForClass( matrix3x4_t ) ); + + ScriptRegisterFunctionNamed( pVM, ScriptFreeMatrixInstance, "FreeMatrixInstance", "Frees an allocated matrix instance." ); + + ScriptRegisterFunctionNamed( pVM, ScriptConcatTransforms, "ConcatTransforms", "Concatenates two transformation matrices into another matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixCopy, "MatrixCopy", "Copies a matrix to another matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixInvert, "MatrixInvert", "Inverts a matrix and copies the result to another matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatricesAreEqual, "MatricesAreEqual", "Checks if two matrices are equal." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixGetColumn, "MatrixGetColumn", "Gets the column of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixSetColumn, "MatrixSetColumn", "Sets the column of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixAngles, "MatrixAngles", "Gets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptAngleMatrix, "AngleMatrix", "Sets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptAngleIMatrix, "AngleIMatrix", "Sets the inverted angles and position of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptSetIdentityMatrix, "SetIdentityMatrix", "Turns a matrix into an identity matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptSetScaleMatrix, "SetScaleMatrix", "Builds a scale matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixScaleBy, "MatrixScaleBy", "Scales a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixScaleByZero, "MatrixScaleByZero", "Scales a matrix by zero." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixGetTranslation, "MatrixGetTranslation", "Gets a matrix's translation." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixSetTranslation, "MatrixSetTranslation", "Sets a matrix's translation." ); + + // + // Quaternion + // + pVM->RegisterClass( GetScriptDescForClass( Quaternion ) ); + + ScriptRegisterFunctionNamed( pVM, ScriptFreeQuaternionInstance, "FreeQuaternionInstance", "Frees an allocated quaternion instance." ); + + ScriptRegisterFunctionNamed( pVM, ScriptQuaternionAdd, "QuaternionAdd", "Adds two quaternions together into another quaternion." ); + ScriptRegisterFunctionNamed( pVM, ScriptMatrixQuaternion, "MatrixQuaternion", "Converts a matrix to a quaternion." ); + ScriptRegisterFunctionNamed( pVM, ScriptQuaternionMatrix, "QuaternionMatrix", "Converts a quaternion to a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptQuaternionAngles, "QuaternionAngles", "Converts a quaternion to angles." ); + + // + // Misc. Vector/QAngle functions + // + ScriptRegisterFunctionNamed( pVM, ScriptAngleVectors, "AngleVectors", "Turns an angle into a direction vector." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorAngles, "VectorAngles", "Turns a direction vector into an angle." ); + + ScriptRegisterFunctionNamed( pVM, ScriptVectorRotate, "VectorRotate", "Rotates a vector with a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorIRotate, "VectorIRotate", "Rotates a vector with the inverse of a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorTransform, "VectorTransform", "Transforms a vector with a matrix." ); + ScriptRegisterFunctionNamed( pVM, ScriptVectorITransform, "VectorITransform", "Transforms a vector with the inverse of a matrix." ); + + ScriptRegisterFunction( pVM, CalcSqrDistanceToAABB, "Returns the squared distance to a bounding box." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcClosestPointOnAABB, "CalcClosestPointOnAABB", "Returns the closest point on a bounding box." ); + ScriptRegisterFunctionNamed( pVM, ScriptCalcDistanceToLine, "CalcDistanceToLine", "Returns the distance to a line." ); + 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/vscript/vscript_bindings_math.h b/vscript/vscript_bindings_math.h new file mode 100644 index 000000000..c2d960b55 --- /dev/null +++ b/vscript/vscript_bindings_math.h @@ -0,0 +1,62 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ================= +// +// Purpose: Shared VScript math functions. +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VSCRIPT_BINDINGS_MATH +#define VSCRIPT_BINDINGS_MATH +#ifdef _WIN32 +#pragma once +#endif + +void RegisterMathBaseBindings( IScriptVM *pVM ); + +// Some base bindings require VM functions +extern IScriptVM *g_pScriptVM; + +//----------------------------------------------------------------------------- +// Exposes matrix3x4_t to VScript +//----------------------------------------------------------------------------- +inline matrix3x4_t *ToMatrix3x4( HSCRIPT hMat ) { return HScriptToClass( hMat ); } + +static void ScriptFreeMatrixInstance( HSCRIPT hMat ) +{ + matrix3x4_t *smatrix = HScriptToClass( hMat ); + if (smatrix) + { + g_pScriptVM->RemoveInstance( hMat ); + delete smatrix; + } +} + +//----------------------------------------------------------------------------- +// Exposes Quaternion to VScript +//----------------------------------------------------------------------------- +class CScriptQuaternionInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); + + ScriptVariant_t *Add( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Subtract( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Multiply( void *p, ScriptVariant_t &variant ); + //ScriptVariant_t *Divide( void *p, ScriptVariant_t &variant ); +}; + +inline Quaternion *ToQuaternion( HSCRIPT hQuat ) { return HScriptToClass( hQuat ); } + +static void ScriptFreeQuaternionInstance( HSCRIPT hQuat ) +{ + Quaternion *squat = HScriptToClass( hQuat ); + if (squat) + { + g_pScriptVM->RemoveInstance( hQuat ); + delete squat; + } +} + +#endif diff --git a/vscript/vscript_squirrel.cpp b/vscript/vscript_squirrel.cpp new file mode 100644 index 000000000..5efb2314a --- /dev/null +++ b/vscript/vscript_squirrel.cpp @@ -0,0 +1,4080 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom implementation of VScript in Source 2013, created from scratch +// using the Alien Swarm SDK as a reference for Valve's library. +// +// Author(s): ReDucTor (header written by Blixibon) +// +// $NoKeywords: $ +//=============================================================================// + +#include "vscript/ivscript.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlmap.h" +#include "tier1/utlstring.h" + +#include "squirrel.h" +#include "sqstdaux.h" +//#include "sqstdblob.h" +//#include "sqstdsystem.h" +#include "sqstdtime.h" +//#include "sqstdio.h" +#include "sqstdmath.h" +#include "sqstdstring.h" + +// HACK: Include internal parts of squirrel for serialization +#include "squirrel/squirrel/sqobject.h" +#include "squirrel/squirrel/sqstate.h" +#include "squirrel/squirrel/sqtable.h" +#include "squirrel/squirrel/sqclass.h" +#include "squirrel/squirrel/sqfuncproto.h" +#include "squirrel/squirrel/sqvm.h" +#include "squirrel/squirrel/sqclosure.h" + +#include "tier1/utlbuffer.h" +#include "tier1/mapbase_con_groups.h" +#include "tier1/convar.h" + +#include "vscript_squirrel.nut" + +#include + +extern ConVar developer; + +struct WriteStateMap +{ + CUtlMap cache; + WriteStateMap() : cache(DefLessFunc(void*)) + {} + + bool CheckCache(CUtlBuffer* pBuffer, void* ptr) + { + auto idx = cache.Find(ptr); + if (idx != cache.InvalidIndex()) + { + pBuffer->PutInt(cache[idx]); + return true; + } + else + { + int newIdx = cache.Count(); + cache.Insert(ptr, newIdx); + pBuffer->PutInt(newIdx); + return false; + } + } +}; + +struct ReadStateMap +{ + CUtlMap cache; +#ifdef _DEBUG + CUtlMap allocated; +#endif + HSQUIRRELVM vm_; + ReadStateMap(HSQUIRRELVM vm) : + cache(DefLessFunc(int)), +#ifdef _DEBUG + allocated(DefLessFunc(int)), +#endif + vm_(vm) + {} + + ~ReadStateMap() + { + FOR_EACH_MAP_FAST(cache, i) + { + HSQOBJECT& obj = cache[i]; + sq_release(vm_, &obj); + } + } + + bool CheckCache(CUtlBuffer* pBuffer, HSQUIRRELVM vm, int * outmarker) + { + int marker = pBuffer->GetInt(); + + auto idx = cache.Find(marker); + +#ifdef _DEBUG + auto allocatedIdx = allocated.Find(marker); + bool hasSeen = allocatedIdx != allocated.InvalidIndex(); + if (!hasSeen) + { + allocated.Insert(marker, true); + } +#endif + + if (idx != cache.InvalidIndex()) + { + sq_pushobject(vm, cache[idx]); + return true; + } + else + { +#ifdef _DEBUG + Assert(!hasSeen); +#endif + *outmarker = marker; + return false; + } + } + + void StoreInCache(int marker, HSQOBJECT& obj) + { + cache.Insert(marker, obj); + } + + void StoreTopInCache(int marker) + { + HSQOBJECT obj; + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + cache.Insert(marker, obj); + } +}; + +class SquirrelVM : public IScriptVM +{ +public: + virtual bool Init() override; + virtual void Shutdown() override; + + virtual bool ConnectDebugger() override; + virtual void DisconnectDebugger() override; + + virtual ScriptLanguage_t GetLanguage() override; + virtual const char* GetLanguageName() override; + + virtual void AddSearchPath(const char* pszSearchPath) override; + + //-------------------------------------------------------- + + virtual bool Frame(float simTime) override; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run(const char* pszScript, bool bWait = true) override; + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript(const char* pszScript, const char* pszId = NULL) override; + virtual void ReleaseScript(HSCRIPT) override; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run(HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true) override; + virtual ScriptStatus_t Run(HSCRIPT hScript, bool bWait) override; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope(const char* pszScope, HSCRIPT hParent = NULL) override; + virtual void ReleaseScope(HSCRIPT hScript) override; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction(const char* pszFunction, HSCRIPT hScope = NULL) override; + virtual void ReleaseFunction(HSCRIPT hScript) override; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) override; + + //-------------------------------------------------------- + // Hooks + //-------------------------------------------------------- + virtual HScriptRaw HScriptToRaw( HSCRIPT val ) override; + virtual ScriptStatus_t ExecuteHookFunction( const char *pszEventName, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) override; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) override; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass(ScriptClassDesc_t* pClassDesc) override; + + //-------------------------------------------------------- + // External constants + //-------------------------------------------------------- + virtual void RegisterConstant(ScriptConstantBinding_t *pScriptConstant) override; + + //-------------------------------------------------------- + // External enums + //-------------------------------------------------------- + virtual void RegisterEnum(ScriptEnumDesc_t *pEnumDesc) override; + + //-------------------------------------------------------- + // External hooks + //-------------------------------------------------------- + virtual void RegisterHook(ScriptHook_t *pHookDesc) override; + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + + virtual HSCRIPT RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct = false) override; + virtual void SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) override; + virtual void RemoveInstance(HSCRIPT hInstance) override; + + virtual void* GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpectedType = NULL) override; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) override; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists(HSCRIPT hScope, const char* pszKey) override; + + virtual bool SetValue(HSCRIPT hScope, const char* pszKey, const char* pszValue) override; + virtual bool SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVariant_t& value) override; + virtual bool SetValue(HSCRIPT hScope, const ScriptVariant_t& key, const ScriptVariant_t& val) override; + + virtual void CreateTable(ScriptVariant_t& Table) override; + virtual int GetNumTableEntries(HSCRIPT hScope) override; + virtual int GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey, ScriptVariant_t* pValue) override; + + virtual bool GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) override; + virtual bool GetValue(HSCRIPT hScope, ScriptVariant_t key, ScriptVariant_t* pValue) override; + virtual void ReleaseValue(ScriptVariant_t& value) override; + + 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 bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) override; + + //---------------------------------------------------------------------------- + + virtual void WriteState(CUtlBuffer* pBuffer) override; + virtual void ReadState(CUtlBuffer* pBuffer) override; + virtual void RemoveOrphanInstances() override; + + virtual void DumpState() override; + + virtual void SetOutputCallback(ScriptOutputFunc_t pFunc) override; + virtual void SetErrorCallback(ScriptErrorFunc_t pFunc) override; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException(const char* pszExceptionText) override; + + + void WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQInteger idx); + void ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState); + HSQUIRRELVM vm_ = nullptr; + HSQOBJECT lastError_; + HSQOBJECT vectorClass_; + HSQOBJECT regexpClass_; +}; + +static char TYPETAG_VECTOR[] = "VectorTypeTag"; + +namespace SQVector +{ + SQInteger Construct(HSQUIRRELVM vm) + { + // TODO: There must be a nicer way to store the data with the actual instance, there are + // default slots but they are really just pointers anyway and you need to hold a member + // pointer which is yet another pointer dereference + int numParams = sq_gettop(vm); + + float x = 0; + float y = 0; + float z = 0; + + if ((numParams >= 2 && SQ_FAILED(sq_getfloat(vm, 2, &x))) || + (numParams >= 3 && SQ_FAILED(sq_getfloat(vm, 3, &y))) || + (numParams >= 4 && SQ_FAILED(sq_getfloat(vm, 4, &z)))) + { + return sq_throwerror(vm, "Expected Vector(float x, float y, float z)"); + } + + SQUserPointer p; + sq_getinstanceup(vm, 1, &p, 0); + new (p) Vector(x, y, z); + + return 0; + } + + SQInteger _get(HSQUIRRELVM vm) + { + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected Vector._get(string)"); + } + + if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + { + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + Vector* v = nullptr; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Unable to get Vector"); + } + + int idx = key[0] - 'x'; + sq_pushfloat(vm, (*v)[idx]); + return 1; + } + + SQInteger _set(HSQUIRRELVM vm) + { + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected Vector._set(string)"); + } + + if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + { + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + Vector* v = nullptr; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Unable to get Vector"); + } + + float val = 0; + if (SQ_FAILED(sq_getfloat(vm, 3, &val))) + { + return sqstd_throwerrorf(vm, "Vector.%s expects float", key); + } + + int idx = key[0] - 'x'; + (*v)[idx] = val; + return 0; + } + + 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)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) + (*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger _sub(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_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) - (*v2)); + sq_remove(vm, -2); + + 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)"); + } + + float s = 0.0; + Vector* v2 = nullptr; + + 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 if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * (*v2)); + sq_remove(vm, -2); + + return 1; + } + else + { + 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)"); + } + + float s = 0.0; + Vector* v2 = nullptr; + + 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 if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) / (*v2)); + sq_remove(vm, -2); + + return 1; + } + else + { + 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)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(-v1->x, -v1->y, -v1->z); + sq_remove(vm, -2); + + return 1; + } + + SQInteger weakref(HSQUIRRELVM vm) + { + sq_weakref(vm, 1); + return 1; + } + + SQInteger getclass(HSQUIRRELVM vm) + { + sq_getclass(vm, 1); + sq_push(vm, -1); + 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; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length()); + return 1; + } + + SQInteger LengthSqr(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)"); + } + + sq_pushfloat(vm, v1->LengthSqr()); + return 1; + } + + SQInteger Length2D(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)"); + } + + sq_pushfloat(vm, v1->Length2D()); + return 1; + } + + SQInteger Length2DSqr(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)"); + } + + sq_pushfloat(vm, v1->Length2DSqr()); + return 1; + } + + SQInteger Normalized(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)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1).Normalized()); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Norm(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)"); + } + + float len = v1->NormalizeInPlace(); + sq_pushfloat(vm, len); + + return 1; + } + + 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_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + return sq_throwerror(vm, "Expected (Vector, float)"); + } + + 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; + } + + SQInteger Dot(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->Dot(*v2)); + return 1; + } + + SQInteger ToKVString(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)"); + } + + sqstd_pushstringf(vm, "%f %f %f", v1->x, v1->y, v1->z); + 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 ) + { + // Return null while invalidating the input vector. + // This allows the user to easily check for input errors without halting. + + 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; + 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_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1).Cross(*v2)); + sq_remove(vm, -2); + + 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; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sqstd_pushstringf(vm, "(Vector %p [%f %f %f])", (void*)v1, v1->x, v1->y, v1->z); + return 1; + } + + SQInteger TypeOf(HSQUIRRELVM vm) + { + sq_pushstring(vm, "Vector", -1); + return 1; + } + + SQInteger Nexti(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)"); + } + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm, 2, &obj); + + const char* curkey = nullptr; + + if (sq_isnull(obj)) + { + curkey = "w"; + } + else if (sq_isstring(obj)) + { + curkey = sq_objtostring(&obj); + } + else + { + return sq_throwerror(vm, "Invalid iterator"); + } + + Assert(curkey && curkey[0] >= 'w' && curkey[0] <= 'z'); + + if (curkey[0] == 'z') + { + // Reached the end + sq_pushnull(vm); + return 1; + } + + char newkey = curkey[0] + 1; + sq_pushstring(vm, &newkey, 1); + + return 1; + } + + 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("_unm"), _unm, 1, _SC(".")}, + {_SC("weakref"), weakref, 1, _SC(".")}, + {_SC("getclass"), getclass, 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(".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("..")}, + + {nullptr,(SQFUNCTION)0,0,nullptr} + }; + + HSQOBJECT register_class(HSQUIRRELVM v) + { + sq_pushstring(v, _SC("Vector"), -1); + sq_newclass(v, SQFalse); + sq_settypetag(v, -1, TYPETAG_VECTOR); + sq_setclassudsize(v, -1, sizeof(Vector)); + SQInteger i = 0; + while (funcs[i].name != 0) { + const SQRegFunction& f = funcs[i]; + sq_pushstring(v, f.name, -1); + sq_newclosure(v, f.f, 0); + sq_setparamscheck(v, f.nparamscheck, f.typemask); + sq_setnativeclosurename(v, -1, f.name); + sq_newslot(v, -3, SQFalse); + i++; + } + HSQOBJECT klass; + sq_resetobject(&klass); + sq_getstackobj(v, -1, &klass); + sq_addref(v, &klass); + sq_newslot(v, -3, SQFalse); + return klass; + } +} // SQVector + +struct ClassInstanceData +{ + ClassInstanceData(void* instance, ScriptClassDesc_t* desc, const char* instanceId = nullptr, bool allowDestruct = false) : + instance(instance), + desc(desc), + instanceId(instanceId), + allowDestruct(allowDestruct) + {} + + void* instance; + ScriptClassDesc_t* desc; + CUtlString instanceId; + + // Indicates this game-created instance is a weak reference and can be destructed (Blixibon) + bool allowDestruct; +}; + +bool CreateParamCheck(const ScriptFunctionBinding_t& func, char* output) +{ + *output++ = '.'; + for (int i = 0; i < func.m_desc.m_Parameters.Count(); ++i) + { + switch (func.m_desc.m_Parameters[i]) + { + case FIELD_FLOAT: + case FIELD_INTEGER: + *output++ = 'n'; + break; + case FIELD_CSTRING: + *output++ = 's'; + break; + case FIELD_VECTOR: + *output++ = 'x'; // Generic instance, we validate on arrival + break; + case FIELD_BOOLEAN: + *output++ = 'b'; + break; + case FIELD_CHARACTER: + *output++ = 's'; + break; + case FIELD_HSCRIPT: + *output++ = '.'; + break; + default: + Assert(!"Unsupported type"); + return false; + }; + } + *output++ = 0; + return true; +} + +void PushVariant(HSQUIRRELVM vm, const ScriptVariant_t& value) +{ + switch (value.m_type) + { + case FIELD_VOID: + sq_pushnull(vm); + break; + case FIELD_FLOAT: + sq_pushfloat(vm, value); + break; + case FIELD_CSTRING: + if ( value.m_pszString ) + sq_pushstring(vm, value, -1); + else + sq_pushnull(vm); + break; + case FIELD_VECTOR: + { + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + sq_pushobject(vm, pSquirrelVM->vectorClass_); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(static_cast(value)); + sq_remove(vm, -2); + break; + } + case FIELD_INTEGER: + sq_pushinteger(vm, value.m_int); + break; + case FIELD_BOOLEAN: + sq_pushbool(vm, value.m_bool); + break; + case FIELD_CHARACTER: + { + char buf[2] = { value.m_char, 0 }; + sq_pushstring(vm, buf, 1); + break; + } + case FIELD_HSCRIPT: + if (value.m_hScript) + { + sq_pushobject(vm, *((HSQOBJECT*)value.m_hScript)); + } + else + { + sq_pushnull(vm); + } + break; + } +} + +void GetVariantScriptString(const ScriptVariant_t& value, char *szValue, int iSize) +{ + switch (value.m_type) + { + case FIELD_VOID: + V_strncpy( szValue, "null", iSize ); + break; + case FIELD_FLOAT: + V_snprintf( szValue, iSize, "%f", value.m_float ); + break; + case FIELD_CSTRING: + V_snprintf( szValue, iSize, "\"%s\"", value.m_pszString ); + break; + case FIELD_VECTOR: + V_snprintf( szValue, iSize, "Vector( %f, %f, %f )", value.m_pVector->x, value.m_pVector->y, value.m_pVector->z ); + break; + case FIELD_INTEGER: + V_snprintf( szValue, iSize, "%i", value.m_int ); + break; + case FIELD_BOOLEAN: + V_snprintf( szValue, iSize, "%d", value.m_bool ); + break; + case FIELD_CHARACTER: + //char buf[2] = { value.m_char, 0 }; + V_snprintf( szValue, iSize, "\"%c\"", value.m_char ); + break; + } +} + +bool getVariant(HSQUIRRELVM vm, SQInteger idx, ScriptVariant_t& variant) +{ + switch (sq_gettype(vm, idx)) + { + case OT_NULL: + { + // TODO: Should this be (HSCRIPT)nullptr + variant.m_type = FIELD_VOID; + return true; + } + case OT_INTEGER: + { + SQInteger val; + if (SQ_FAILED(sq_getinteger(vm, idx, &val))) + { + return false; + } + variant = (int)val; + return true; + } + case OT_FLOAT: + { + SQFloat val; + if (SQ_FAILED(sq_getfloat(vm, idx, &val))) + { + return false; + } + variant = (float)val; + return true; + } + case OT_BOOL: + { + SQBool val; + if (SQ_FAILED(sq_getbool(vm, idx, &val))) + { + return false; + } + variant = val ? true : false; + return true; + } + case OT_STRING: + { + const char* val; + SQInteger size = 0; + if (SQ_FAILED(sq_getstringandsize(vm, idx, &val, &size))) + { + return false; + } + char* buffer = new char[size + 1]; + V_memcpy(buffer, val, size); + buffer[size] = 0; + variant = buffer; + variant.m_flags |= SV_FREE; + return true; + } + case OT_INSTANCE: + { + Vector* v = nullptr; + SQUserPointer tag; + if (SQ_SUCCEEDED(sq_gettypetag(vm, idx, &tag)) && + tag == TYPETAG_VECTOR && + SQ_SUCCEEDED(sq_getinstanceup(vm, idx, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + variant = new Vector(*v); + variant.m_flags |= SV_FREE; + return true; + } + // fall-through for non-vector + } + default: + { + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm, idx, obj); + sq_addref(vm, obj); + variant = (HSCRIPT)obj; + } + }; + + + return true; +} + +SQInteger function_stub(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + SQUserPointer userptr = nullptr; + sq_getuserpointer(vm, top, &userptr); + + Assert(userptr); + + ScriptFunctionBinding_t* pFunc = (ScriptFunctionBinding_t*)userptr; + + auto nargs = pFunc->m_desc.m_Parameters.Count(); + + if (nargs > top) + { + // TODO: Handle optional parameters? + return sq_throwerror(vm, "Invalid number of parameters"); + } + + CUtlVector params; + params.SetCount(nargs); + + for (int i = 0; i < nargs; ++i) + { + switch (pFunc->m_desc.m_Parameters[i]) + { + case FIELD_FLOAT: + { + float val = 0.0; + if (SQ_FAILED(sq_getfloat(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected float"); + params[i] = val; + break; + } + case FIELD_CSTRING: + { + const char* val; + if (SQ_FAILED(sq_getstring(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + params[i] = val; + break; + } + case FIELD_VECTOR: + { + Vector* val; + if (SQ_FAILED(sq_getinstanceup(vm, i + 2, (SQUserPointer*)&val, TYPETAG_VECTOR))) + return sq_throwerror(vm, "Expected Vector"); + params[i] = *val; + break; + } + case FIELD_INTEGER: + { + SQInteger val = 0; + if (SQ_FAILED(sq_getinteger(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected integer"); + params[i] = (int)val; + break; + } + case FIELD_BOOLEAN: + { + SQBool val = 0; + if (SQ_FAILED(sq_getbool(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected bool"); + params[i] = val ? true : false; + break; + } + case FIELD_CHARACTER: + { + const char* val; + if (SQ_FAILED(sq_getstring(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + params[i] = val[i]; + break; + } + case FIELD_HSCRIPT: + { + HSQOBJECT val; + if (SQ_FAILED(sq_getstackobj(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected handle"); + + if (sq_isnull(val)) + { + params[i] = (HSCRIPT)nullptr; + } + else + { + HSQOBJECT* pObject = new HSQOBJECT; + *pObject = val; + sq_addref(vm, pObject); + params[i] = (HSCRIPT)pObject; + } + break; + } + default: + Assert(!"Unsupported type"); + return false; + } + } + + void* instance = nullptr; + + if (pFunc->m_flags & SF_MEMBER_FUNC) + { + SQUserPointer self; + sq_getinstanceup(vm, 1, &self, nullptr); + + if (!self) + { + return sq_throwerror(vm, "Accessed null instance"); + } + + instance = ((ClassInstanceData*)self)->instance; + } + + ScriptVariant_t retval; + + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + + sq_resetobject(&pSquirrelVM->lastError_); + + (*pFunc->m_pfnBinding)(pFunc->m_pFunction, instance, params.Base(), nargs, + pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval); + + if (!sq_isnull(pSquirrelVM->lastError_)) + { + sq_pushobject(vm, pSquirrelVM->lastError_); + sq_resetobject(&pSquirrelVM->lastError_); + return sq_throwobject(vm); + } + + PushVariant(vm, retval); + + if (retval.m_type == FIELD_VECTOR) + delete retval.m_pVector; + + return pFunc->m_desc.m_ReturnType != FIELD_VOID; +} + + +SQInteger destructor_stub(SQUserPointer p, SQInteger size) +{ + auto classInstanceData = (ClassInstanceData*)p; + + if (classInstanceData->desc->m_pfnDestruct) + classInstanceData->desc->m_pfnDestruct(classInstanceData->instance); + + classInstanceData->~ClassInstanceData(); + return 0; +} + +SQInteger destructor_stub_instance(SQUserPointer p, SQInteger size) +{ + auto classInstanceData = (ClassInstanceData*)p; + // We don't call destructor here because this is owned by the game + classInstanceData->~ClassInstanceData(); + return 0; +} + +SQInteger constructor_stub(HSQUIRRELVM vm) +{ + ScriptClassDesc_t* pClassDesc = nullptr; + sq_gettypetag(vm, 1, (SQUserPointer*)&pClassDesc); + + if (!pClassDesc->m_pfnConstruct) + { + return sqstd_throwerrorf(vm, "Unable to construct instances of %s", pClassDesc->m_pszScriptName); + } + + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + + sq_resetobject(&pSquirrelVM->lastError_); + + void* instance = pClassDesc->m_pfnConstruct(); + + if (!sq_isnull(pSquirrelVM->lastError_)) + { + sq_pushobject(vm, pSquirrelVM->lastError_); + sq_resetobject(&pSquirrelVM->lastError_); + return sq_throwobject(vm); + } + + { + SQUserPointer p; + sq_getinstanceup(vm, 1, &p, 0); + new(p) ClassInstanceData(instance, pClassDesc, nullptr, true); + } + + sq_setreleasehook(vm, 1, &destructor_stub); + + return 0; +} + +SQInteger tostring_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + char buffer[128] = ""; + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->ToString(classInstanceData->instance, buffer, sizeof(buffer))) + { + sq_pushstring(vm, buffer, -1); + } + else if (classInstanceData) + { + sqstd_pushstringf(vm, "(%s : 0x%p)", classInstanceData->desc->m_pszScriptName, classInstanceData->instance); + } + else + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm, 1, &obj); + // Semi-based on SQVM::ToString default case + sqstd_pushstringf(vm, "(%s: 0x%p)", IdType2Name(obj._type), (void*)_rawval(obj)); + } + + return 1; +} + +SQInteger get_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected _get(string)"); + } + + ScriptVariant_t var; + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->Get(classInstanceData->instance, key, var)) + { + PushVariant(vm, var); + } + else + { + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + return 1; +} + +SQInteger set_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected _set(string)"); + } + + ScriptVariant_t var; + getVariant( vm, -1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->Set(classInstanceData->instance, key, var)) + { + sq_pop(vm, 1); + } + else + { + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } + + return 0; +} + +SQInteger add_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op +"); +} + +SQInteger sub_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Subtract( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op -"); +} + +SQInteger mul_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper ) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op *"); +} + +SQInteger div_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + ScriptVariant_t var; + getVariant( vm, 1, var ); + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper ) + { + ScriptVariant_t *result = classInstanceData->desc->pHelper->Add( classInstanceData->instance, var ); + if (result != nullptr) + { + PushVariant( vm, *result ); + sq_pop(vm, 1); + return 1; + } + } + + sq_pop(vm, 1); + return sqstd_throwerrorf(vm, "invalid arith op /"); +} + +SQInteger IsValid_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + sq_pushbool(vm, classInstanceData != nullptr); + return 1; +} + +SQInteger weakref_stub(HSQUIRRELVM vm) +{ + sq_weakref(vm, 1); + return 1; +} + +SQInteger getclass_stub(HSQUIRRELVM vm) +{ + sq_getclass(vm, 1); + sq_push(vm, -1); + return 1; +} + +struct SquirrelSafeCheck +{ + SquirrelSafeCheck(HSQUIRRELVM vm, int outputCount = 0) : + vm_{ vm }, + top_{ sq_gettop(vm) }, + outputCount_{ outputCount } + {} + + ~SquirrelSafeCheck() + { + SQInteger curtop = sq_gettop(vm_); + SQInteger diff = curtop - outputCount_; + if ( top_ != diff ) + { + Assert(!"Squirrel VM stack is not consistent"); + Error("Squirrel VM stack is not consistent\n"); + } + + // TODO: Handle error state checks + } + + HSQUIRRELVM vm_; + SQInteger top_; + SQInteger outputCount_; +}; + + +void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) +{ + va_list args; + char buffer[2048]; + va_start(args, format); + V_vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + CGMsg(0, CON_GROUP_VSCRIPT_PRINT, "%s", buffer); +} + +void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) +{ + va_list args; + char buffer[2048]; + va_start(args, format); + V_vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + Warning("%s", buffer); +} + +const char * ScriptDataTypeToName(ScriptDataType_t datatype) +{ + switch (datatype) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "string"; + case FIELD_VECTOR: return "Vector"; + case FIELD_INTEGER: return "int"; + case FIELD_BOOLEAN: return "bool"; + case FIELD_CHARACTER: return "char"; + case FIELD_HSCRIPT: return "handle"; + case FIELD_VARIANT: return "variant"; + default: return ""; + } +} + + +#define PushDocumentationRegisterFunction( szName ) \ + sq_pushroottable(vm); \ + sq_pushstring(vm, "__Documentation", -1); \ + sq_get(vm, -2); \ + sq_pushstring(vm, szName, -1); \ + sq_get(vm, -2); \ + sq_push(vm, -2); + +#define CallDocumentationRegisterFunction( paramcount ) \ + sq_call(vm, paramcount+1, SQFalse, SQFalse); \ + sq_pop(vm, 3); + +void RegisterDocumentation(HSQUIRRELVM vm, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + if ( !developer.GetInt() ) + return; + + SquirrelSafeCheck safeCheck(vm); + + if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, "::"); + } + + V_strcat_safe(name, pFuncDesc.m_pszScriptName); + + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s(", ScriptDataTypeToName(pFuncDesc.m_ReturnType), name); + + for (int i = 0; i < pFuncDesc.m_Parameters.Count(); ++i) + { + if (i != 0) + V_strcat_safe(signature, ", "); + + V_strcat_safe(signature, ScriptDataTypeToName(pFuncDesc.m_Parameters[i])); + } + + V_strcat_safe(signature, ")"); + + // RegisterHelp(name, signature, description) + PushDocumentationRegisterFunction( "RegisterHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); +} + +void RegisterClassDocumentation(HSQUIRRELVM vm, const ScriptClassDesc_t* pClassDesc) +{ + if ( !developer.GetInt() ) + return; + + SquirrelSafeCheck safeCheck(vm); + + const char *name = pClassDesc->m_pszScriptName; + const char *base = ""; + if (pClassDesc->m_pBaseDesc) + { + base = pClassDesc->m_pBaseDesc->m_pszScriptName; + } + + const char *description = pClassDesc->m_pszDescription; + if (description) + { + if (description[0] == SCRIPT_HIDE[0]) + return; + if (description[0] == SCRIPT_SINGLETON[0]) + description++; + } + else + { + description = ""; + } + + // RegisterClassHelp(name, base, description) + PushDocumentationRegisterFunction( "RegisterClassHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, base, -1); + sq_pushstring(vm, description, -1); + CallDocumentationRegisterFunction( 3 ); +} + +void RegisterEnumDocumentation(HSQUIRRELVM vm, const ScriptEnumDesc_t* pClassDesc) +{ + if ( !developer.GetInt() ) + return; + + SquirrelSafeCheck safeCheck(vm); + + if (pClassDesc->m_pszDescription && pClassDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + const char *name = pClassDesc->m_pszScriptName; + + // RegisterEnumHelp(name, description) + PushDocumentationRegisterFunction( "RegisterEnumHelp" ); + sq_pushstring(vm, name, -1); + sq_pushinteger(vm, pClassDesc->m_ConstantBindings.Count()); + sq_pushstring(vm, pClassDesc->m_pszDescription ? pClassDesc->m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); +} + +void RegisterConstantDocumentation( HSQUIRRELVM vm, const ScriptConstantBinding_t* pConstDesc, const char *pszAsString, ScriptEnumDesc_t* pEnumDesc = nullptr ) +{ + if ( !developer.GetInt() ) + return; + + SquirrelSafeCheck safeCheck(vm); + + if (pConstDesc->m_pszDescription && pConstDesc->m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pEnumDesc) + { + V_strcat_safe(name, pEnumDesc->m_pszScriptName); + V_strcat_safe(name, "."); + } + + V_strcat_safe(name, pConstDesc->m_pszScriptName); + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s (%s)", pszAsString, ScriptDataTypeToName(pConstDesc->m_data.m_type)); + + // RegisterConstHelp(name, signature, description) + PushDocumentationRegisterFunction( "RegisterConstHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pConstDesc->m_pszDescription ? pConstDesc->m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); +} + +void RegisterHookDocumentation(HSQUIRRELVM vm, const ScriptHook_t* pHook, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + if ( !developer.GetInt() ) + return; + + SquirrelSafeCheck safeCheck(vm); + + if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, " -> "); + } + + V_strcat_safe(name, pFuncDesc.m_pszScriptName); + + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s(", ScriptDataTypeToName(pFuncDesc.m_ReturnType), name); + + for (int i = 0; i < pFuncDesc.m_Parameters.Count(); ++i) + { + if (i != 0) + V_strcat_safe(signature, ", "); + + V_strcat_safe(signature, ScriptDataTypeToName(pFuncDesc.m_Parameters[i])); + V_strcat_safe(signature, " ["); + V_strcat_safe(signature, pHook->m_pszParameterNames[i]); + V_strcat_safe(signature, "]"); + } + + V_strcat_safe(signature, ")"); + + // RegisterHookHelp(name, signature, description) + PushDocumentationRegisterFunction( "RegisterHookHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); +} + +void RegisterMemberDocumentation(HSQUIRRELVM vm, const ScriptMemberDesc_t& pDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + if ( !developer.GetInt() ) + return; + + SquirrelSafeCheck safeCheck(vm); + + if (pDesc.m_pszDescription && pDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, "."); + } + + if (pDesc.m_pszScriptName) + V_strcat_safe(name, pDesc.m_pszScriptName); + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s", ScriptDataTypeToName(pDesc.m_ReturnType), name); + + // RegisterMemberHelp(name, signature, description) + PushDocumentationRegisterFunction( "RegisterMemberHelp" ); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pDesc.m_pszDescription ? pDesc.m_pszDescription : "", -1); + CallDocumentationRegisterFunction( 3 ); +} + +SQInteger GetDeveloperLevel(HSQUIRRELVM vm) +{ + sq_pushinteger( vm, developer.GetInt() ); + return 1; +} + + +bool SquirrelVM::Init() +{ + vm_ = sq_open(1024); //creates a VM with initial stack size 1024 + + if (vm_ == nullptr) + return false; + + sq_setforeignptr(vm_, this); + sq_resetobject(&lastError_); + + sq_setprintfunc(vm_, printfunc, errorfunc); + + + { + sq_pushroottable(vm_); + + sqstd_register_mathlib(vm_); + sqstd_register_stringlib(vm_); + vectorClass_ = SQVector::register_class(vm_); + + // We exclude these libraries as they create a security risk on the client even + // though I'm sure if someone tried hard enough they could achieve all sorts of + // things with the other APIs, this just makes it a little bit harder for a map + // created by someone in the community causing a bunch of security vulnerablilties. + // + // Pretty sure DoIncludeScript() is already a vulnerability vector here, however + // that also depends on compile errors not showing up and relies on IFilesystem with + // a path prefix. + // + //sqstd_register_bloblib(vm_); + //sqstd_register_iolib(vm_); + //sqstd_register_systemlib(vm_); + + // There is no vulnerability in getting time. + sqstd_register_timelib(vm_); + + + sqstd_seterrorhandlers(vm_); + + { + // Unfortunately we can not get the pattern from a regexp instance + // so we need to wrap it with our own to get it. + if (Run(R"script( + class regexp extends regexp + { + constructor(pattern) + { + base.constructor(pattern); + this.pattern_ = pattern; + } + pattern_ = ""; + } + )script") == SCRIPT_ERROR) + { + this->Shutdown(); + return false; + } + + sq_resetobject(®expClass_); + sq_pushstring(vm_, "regexp", -1); + sq_rawget(vm_, -2); + sq_getstackobj(vm_, -1, ®expClass_); + sq_addref(vm_, ®expClass_); + sq_pop(vm_, 1); + } + + sq_pushstring( vm_, "developer", -1 ); + sq_newclosure( vm_, &GetDeveloperLevel, 0 ); + //sq_setnativeclosurename( vm_, -1, "developer" ); + sq_newslot( vm_, -3, SQFalse ); + + sq_pop(vm_, 1); + } + + if (Run(g_Script_vscript_squirrel) != SCRIPT_DONE) + { + this->Shutdown(); + return false; + } + + return true; +} + +void SquirrelVM::Shutdown() +{ + if (vm_) + { + sq_release(vm_, &vectorClass_); + sq_release(vm_, ®expClass_); + + sq_close(vm_); + vm_ = nullptr; + } +} + +bool SquirrelVM::ConnectDebugger() +{ + // TODO: Debugger support + return false; +} + +void SquirrelVM::DisconnectDebugger() +{ + // TODO: Debugger support +} + +ScriptLanguage_t SquirrelVM::GetLanguage() +{ + return SL_SQUIRREL; +} + +const char* SquirrelVM::GetLanguageName() +{ + return "squirrel"; +} + +void SquirrelVM::AddSearchPath(const char* pszSearchPath) +{ + // TODO: Search path support +} + +bool SquirrelVM::Frame(float simTime) +{ + // TODO: Frame support + return false; +} + +ScriptStatus_t SquirrelVM::Run(const char* pszScript, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), "", SQTrue))) + { + return SCRIPT_ERROR; + } + + sq_pushroottable(vm_); + if (SQ_FAILED(sq_call(vm_, 1, SQFalse, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +HSCRIPT SquirrelVM::CompileScript(const char* pszScript, const char* pszId) +{ + SquirrelSafeCheck safeCheck(vm_); + + Assert(vm_); + if (pszId == nullptr) pszId = ""; + if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), pszId, SQTrue))) + { + return nullptr; + } + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + return (HSCRIPT)obj; +} + +void SquirrelVM::ReleaseScript(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; +} + +ScriptStatus_t SquirrelVM::Run(HSCRIPT hScript, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + auto result = sq_call(vm_, 1, false, true); + sq_pop(vm_, 1); + if (SQ_FAILED(result)) + { + return SCRIPT_ERROR; + } + return SCRIPT_DONE; +} + +ScriptStatus_t SquirrelVM::Run(HSCRIPT hScript, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + sq_pushroottable(vm_); + auto result = sq_call(vm_, 1, false, true); + sq_pop(vm_, 1); + if (SQ_FAILED(result)) + { + return SCRIPT_ERROR; + } + return SCRIPT_DONE; +} + +HSCRIPT SquirrelVM::CreateScope(const char* pszScope, HSCRIPT hParent) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_newtable(vm_); + + if (hParent) + { + HSQOBJECT* parent = (HSQOBJECT*)hParent; + Assert(hParent != INVALID_HSCRIPT); + sq_pushobject(vm_, *parent); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszScope, -1); + sq_push(vm_, -3); + sq_rawset(vm_, -3); + + sq_pushstring(vm_, "__vname", -1); + sq_pushstring(vm_, pszScope, -1); + sq_rawset(vm_, -4); + + if (SQ_FAILED(sq_setdelegate(vm_, -2))) + { + sq_pop(vm_, 2); + return nullptr; + } + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + return (HSCRIPT)obj; +} + +void SquirrelVM::ReleaseScope(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + + sq_getdelegate(vm_, -1); + + sq_pushstring(vm_, "__vname", -1); + sq_rawdeleteslot(vm_, -2, SQFalse); + + sq_pop(vm_, 2); + + sq_release(vm_, obj); + delete obj; +} + +HSCRIPT SquirrelVM::LookupFunction(const char* pszFunction, HSCRIPT hScope) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + sq_pushstring(vm_, _SC(pszFunction), -1); + + HSQOBJECT obj; + sq_resetobject(&obj); + + if (sq_get(vm_, -2) == SQ_OK) + { + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + sq_pop(vm_, 1); + } + sq_pop(vm_, 1); + + if (!sq_isclosure(obj)) + { + sq_release(vm_, &obj); + return nullptr; + } + + HSQOBJECT* pObj = new HSQOBJECT; + *pObj = obj; + return (HSCRIPT)pObj; +} + +void SquirrelVM::ReleaseFunction(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; +} + +ScriptStatus_t SquirrelVM::ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hFunction) + return SCRIPT_ERROR; + + if (hFunction == INVALID_HSCRIPT) + return SCRIPT_ERROR; + + HSQOBJECT* pFunc = (HSQOBJECT*)hFunction; + sq_pushobject(vm_, *pFunc); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + for (int i = 0; i < nArgs; ++i) + { + PushVariant(vm_, pArgs[i]); + } + + bool hasReturn = pReturn != nullptr; + + if (SQ_FAILED(sq_call(vm_, nArgs + 1, hasReturn, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + if (hasReturn) + { + if (!getVariant(vm_, -1, *pReturn)) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +HScriptRaw SquirrelVM::HScriptToRaw( HSCRIPT val ) +{ + Assert( val ); + Assert( val != INVALID_HSCRIPT ); + + HSQOBJECT *obj = (HSQOBJECT*)val; +#if 0 + if ( sq_isweakref(*obj) ) + return obj->_unVal.pWeakRef->_obj._unVal.raw; +#endif + return obj->_unVal.raw; +} + +ScriptStatus_t SquirrelVM::ExecuteHookFunction(const char *pszEventName, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT* pFunc = (HSQOBJECT*)GetScriptHookManager().GetHookFunction(); + sq_pushobject(vm_, *pFunc); + + // The call environment of the Hooks::Call function does not matter + // as the function does not access any member variables. + sq_pushroottable(vm_); + + sq_pushstring(vm_, pszEventName, -1); + + if (hScope) + sq_pushobject(vm_, *((HSQOBJECT*)hScope)); + else + sq_pushnull(vm_); // global hook + + for (int i = 0; i < nArgs; ++i) + { + PushVariant(vm_, pArgs[i]); + } + + bool hasReturn = pReturn != nullptr; + + if (SQ_FAILED(sq_call(vm_, nArgs + 3, hasReturn, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + if (hasReturn) + { + if (!getVariant(vm_, -1, *pReturn)) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +void SquirrelVM::RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pScriptFunction); + + if (!pScriptFunction) + return; + + char typemask[64]; + if (!CreateParamCheck(*pScriptFunction, typemask)) + { + return; + } + + sq_pushroottable(vm_); + + sq_pushstring(vm_, pScriptFunction->m_desc.m_pszScriptName, -1); + + sq_pushuserpointer(vm_, pScriptFunction); + sq_newclosure(vm_, function_stub, 1); + + sq_setnativeclosurename(vm_, -1, pScriptFunction->m_desc.m_pszScriptName); + + sq_setparamscheck(vm_, pScriptFunction->m_desc.m_Parameters.Count() + 1, typemask); + bool isStatic = false; + sq_newslot(vm_, -3, isStatic); + + sq_pop(vm_, 1); + + RegisterDocumentation(vm_, pScriptFunction->m_desc); +} + +bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_pushroottable(vm_); + sq_pushstring(vm_, pClassDesc->m_pszScriptName, -1); + + // Check if class name is already taken + if (sq_get(vm_, -2) == SQ_OK) + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + if (!sq_isnull(obj)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 1); + } + + // Register base in case it doesn't exist + if (pClassDesc->m_pBaseDesc) + { + RegisterClass(pClassDesc->m_pBaseDesc); + + // Check if the base class can be + sq_pushstring(vm_, pClassDesc->m_pBaseDesc->m_pszScriptName, -1); + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + } + + if (SQ_FAILED(sq_newclass(vm_, pClassDesc->m_pBaseDesc ? SQTrue : SQFalse))) + { + sq_pop(vm_, 2 + (pClassDesc->m_pBaseDesc ? 1 : 0)); + return false; + } + + sq_settypetag(vm_, -1, pClassDesc); + + sq_setclassudsize(vm_, -1, sizeof(ClassInstanceData)); + + sq_pushstring(vm_, "constructor", -1); + sq_newclosure(vm_, constructor_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_tostring", -1); + sq_newclosure(vm_, tostring_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_get", -1); + sq_newclosure(vm_, get_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_set", -1); + sq_newclosure(vm_, set_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_add", -1); + sq_newclosure(vm_, add_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_sub", -1); + sq_newclosure(vm_, sub_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_mul", -1); + sq_newclosure(vm_, mul_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_div", -1); + sq_newclosure(vm_, div_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "IsValid", -1); + sq_newclosure(vm_, IsValid_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "weakref", -1); + sq_newclosure(vm_, weakref_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "getclass", -1); + sq_newclosure(vm_, getclass_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + + for (int i = 0; i < pClassDesc->m_FunctionBindings.Count(); ++i) + { + auto& scriptFunction = pClassDesc->m_FunctionBindings[i]; + + char typemask[64]; + if (!CreateParamCheck(scriptFunction, typemask)) + { + Warning("Unable to create param check for %s.%s\n", + pClassDesc->m_pszClassname, scriptFunction.m_desc.m_pszFunction); + break; + } + + sq_pushstring(vm_, scriptFunction.m_desc.m_pszScriptName, -1); + + sq_pushuserpointer(vm_, &scriptFunction); + sq_newclosure(vm_, function_stub, 1); + + sq_setnativeclosurename(vm_, -1, scriptFunction.m_desc.m_pszScriptName); + + sq_setparamscheck(vm_, scriptFunction.m_desc.m_Parameters.Count() + 1, typemask); + bool isStatic = false; + sq_newslot(vm_, -3, isStatic); + + RegisterDocumentation(vm_, scriptFunction.m_desc, pClassDesc); + } + + for (int i = 0; i < pClassDesc->m_Hooks.Count(); ++i) + { + auto& scriptHook = pClassDesc->m_Hooks[i]; + + RegisterHookDocumentation(vm_, scriptHook, scriptHook->m_desc, pClassDesc); + } + + for (int i = 0; i < pClassDesc->m_Members.Count(); ++i) + { + auto& member = pClassDesc->m_Members[i]; + + RegisterMemberDocumentation(vm_, member, pClassDesc); + } + + sq_pushstring(vm_, pClassDesc->m_pszScriptName, -1); + sq_push(vm_, -2); + + if (SQ_FAILED(sq_newslot(vm_, -4, SQFalse))) + { + sq_pop(vm_, 4); + return false; + } + + sq_pop(vm_, 2); + + RegisterClassDocumentation(vm_, pClassDesc); + + return true; +} + +void SquirrelVM::RegisterConstant(ScriptConstantBinding_t* pScriptConstant) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pScriptConstant); + + if (!pScriptConstant) + return; + + sq_pushconsttable(vm_); + sq_pushstring(vm_, pScriptConstant->m_pszScriptName, -1); + + PushVariant(vm_, pScriptConstant->m_data); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + + char szValue[64]; + GetVariantScriptString( pScriptConstant->m_data, szValue, sizeof(szValue) ); + RegisterConstantDocumentation(vm_, pScriptConstant, szValue); +} + +void SquirrelVM::RegisterEnum(ScriptEnumDesc_t* pEnumDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pEnumDesc); + + if (!pEnumDesc) + return; + + sq_newtableex(vm_, pEnumDesc->m_ConstantBindings.Count()); + + sq_pushconsttable(vm_); + + sq_pushstring(vm_, pEnumDesc->m_pszScriptName, -1); + sq_push(vm_, -3); + sq_rawset(vm_, -3); + + for (int i = 0; i < pEnumDesc->m_ConstantBindings.Count(); ++i) + { + auto& scriptConstant = pEnumDesc->m_ConstantBindings[i]; + + sq_pushstring(vm_, scriptConstant.m_pszScriptName, -1); + PushVariant(vm_, scriptConstant.m_data); + sq_rawset(vm_, -4); + + char szValue[64]; + GetVariantScriptString( scriptConstant.m_data, szValue, sizeof(szValue) ); + RegisterConstantDocumentation(vm_, &scriptConstant, szValue, pEnumDesc); + } + + sq_pop(vm_, 2); + + RegisterEnumDocumentation(vm_, pEnumDesc); +} + +void SquirrelVM::RegisterHook(ScriptHook_t* pHookDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pHookDesc); + + if (!pHookDesc) + return; + + RegisterHookDocumentation(vm_, pHookDesc, pHookDesc->m_desc, nullptr); +} + +HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance, bool bAllowDestruct) +{ + SquirrelSafeCheck safeCheck(vm_); + + this->RegisterClass(pDesc); + + sq_pushroottable(vm_); + sq_pushstring(vm_, pDesc->m_pszScriptName, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return nullptr; + } + + if (SQ_FAILED(sq_createinstance(vm_, -1))) + { + sq_pop(vm_, 2); + return nullptr; + } + + { + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) ClassInstanceData(pInstance, pDesc, nullptr, bAllowDestruct); + } + + sq_setreleasehook(vm_, -1, bAllowDestruct ? &destructor_stub : &destructor_stub_instance); + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 3); + + return (HSCRIPT)obj; +} + +void SquirrelVM::SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + sq_pushobject(vm_, *obj); + + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + + auto classInstanceData = (ClassInstanceData*)self; + + classInstanceData->instanceId = pszId; + + sq_poptop(vm_); +} + +void SquirrelVM::RemoveInstance(HSCRIPT hInstance) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + sq_pushobject(vm_, *obj); + + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + + ((ClassInstanceData*)self)->~ClassInstanceData(); + + sq_setinstanceup(vm_, -1, nullptr); + sq_setreleasehook(vm_, -1, nullptr); + sq_pop(vm_, 1); + + sq_release(vm_, obj); + delete obj; +} + +void* SquirrelVM::GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpectedType) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return nullptr; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + + if (pExpectedType) + { + sq_pushroottable(vm_); + sq_pushstring(vm_, pExpectedType->m_pszScriptName, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return nullptr; + } + + sq_pushobject(vm_, *obj); + + if (sq_instanceof(vm_) != SQTrue) + { + sq_pop(vm_, 3); + return nullptr; + } + + sq_pop(vm_, 3); + } + + sq_pushobject(vm_, *obj); + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + sq_pop(vm_, 1); + + auto classInstanceData = (ClassInstanceData*)self; + + if (!classInstanceData) + { + return nullptr; + } + + + return classInstanceData->instance; +} + +bool SquirrelVM::GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) +{ + static int keyIdx = 0; + // This gets used for script scope, still confused why it needs to be inside IScriptVM + // is it just to be a compatible name for CreateScope? + V_snprintf(pBuf, nBufSize, "%08X_%s", ++keyIdx, pszRoot); + return true; +} + +bool SquirrelVM::ValueExists(HSCRIPT hScope, const char* pszKey) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 2); + return true; +} + +bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const char* pszValue) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + sq_pushstring(vm_, pszValue, -1); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + return true; +} + +bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVariant_t& value) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + PushVariant(vm_, value); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + return true; +} + +bool SquirrelVM::SetValue( HSCRIPT hScope, const ScriptVariant_t& key, const ScriptVariant_t& val ) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT obj = *(HSQOBJECT*)hScope; + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, obj); + } + else + { + sq_pushroottable(vm_); + } + + if ( sq_isarray(obj) ) + { + Assert( key.m_type == FIELD_INTEGER ); + + sq_pushinteger(vm_, key.m_int); + PushVariant(vm_, val); + + sq_set(vm_, -3); + } + else + { + PushVariant(vm_, key); + PushVariant(vm_, val); + + sq_newslot(vm_, -3, SQFalse); + } + + sq_pop(vm_, 1); + return true; +} + +void SquirrelVM::CreateTable(ScriptVariant_t& Table) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_newtable(vm_); + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + Table = (HSCRIPT)obj; +} + +// +// input table/array/class/instance/string +// +int SquirrelVM::GetNumTableEntries(HSCRIPT hScope) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hScope) + { + // sq_getsize returns -1 on invalid input + return -1; + } + + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + + int count = sq_getsize(vm_, -1); + + sq_pop(vm_, 1); + + return count; +} + +int SquirrelVM::GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey, ScriptVariant_t* pValue) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + if (nIterator == -1) + { + sq_pushnull(vm_); + } + else + { + sq_pushinteger(vm_, nIterator); + } + + SQInteger nextiter = -1; + + if (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + if (pKey) getVariant(vm_, -2, *pKey); + if (pValue) getVariant(vm_, -1, *pValue); + + sq_getinteger(vm_, -3, &nextiter); + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + return nextiter; +} + +bool SquirrelVM::GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) +{ +#ifdef _DEBUG + AssertMsg( pszKey, "FATAL: cannot get NULL" ); + + // Don't crash on debug + if ( !pszKey ) + return GetValue( hScope, ScriptVariant_t(0), pValue ); +#endif + + SquirrelSafeCheck safeCheck(vm_); + + Assert(pValue); + if (!pValue) + { + return false; + } + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + if (!getVariant(vm_, -1, *pValue)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 2); + + return true; +} + +bool SquirrelVM::GetValue( HSCRIPT hScope, ScriptVariant_t key, ScriptVariant_t* pValue ) +{ + SquirrelSafeCheck safeCheck(vm_); + + Assert(pValue); + if (!pValue) + { + return false; + } + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + PushVariant(vm_, key); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + if (!getVariant(vm_, -1, *pValue)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 2); + + return true; +} + + +void SquirrelVM::ReleaseValue(ScriptVariant_t& value) +{ + SquirrelSafeCheck safeCheck(vm_); + if (value.m_type == FIELD_HSCRIPT) + { + HSCRIPT hScript = value; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; + } + else + { + value.Free(); + } + + // Let's prevent this being called again and giving some UB + value.m_type = FIELD_VOID; +} + +bool SquirrelVM::ClearValue(HSCRIPT hScope, const char* pszKey) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + if (SQ_FAILED(sq_deleteslot(vm_, -2, SQFalse))) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 1); + return true; +} + +bool SquirrelVM::ClearValue(HSCRIPT hScope, ScriptVariant_t pKey) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + PushVariant(vm_, pKey); + if (SQ_FAILED(sq_deleteslot(vm_, -2, SQFalse))) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 1); + return true; +} + + +void SquirrelVM::CreateArray(ScriptVariant_t &arr, int size) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT *obj = new HSQOBJECT; + sq_resetobject(obj); + + sq_newarray(vm_,size); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + arr = (HSCRIPT)obj; +} + +bool SquirrelVM::ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT *arr = (HSQOBJECT*)hArray; + + sq_pushobject(vm_, *arr); + PushVariant(vm_, val); + bool ret = sq_arrayappend(vm_, -2) == SQ_OK; + sq_pop(vm_, 1); + + return ret; +} + +enum ClassType +{ + VectorClassType = 0, + NativeClassType = 1, + ScriptClassType = 2 +}; + +SQInteger closure_write(SQUserPointer file, SQUserPointer p, SQInteger size) +{ + ((CUtlBuffer*)file)->Put(p, size); + return size; +} + +void SquirrelVM::WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQInteger idx) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, idx, &obj); + + switch (obj._type) + { + case OT_NULL: + { + pBuffer->PutInt(OT_NULL); + break; + } + case OT_INTEGER: + { + pBuffer->PutInt(OT_INTEGER); + pBuffer->PutInt64(sq_objtointeger(&obj)); + break; + } + case OT_FLOAT: + { + pBuffer->PutInt(OT_FLOAT); + pBuffer->PutFloat(sq_objtofloat(&obj)); + break; + } + case OT_BOOL: + { + pBuffer->PutInt(OT_BOOL); + pBuffer->PutChar(sq_objtobool(&obj)); + break; + } + case OT_STRING: + { + pBuffer->PutInt(OT_STRING); + const char* val = nullptr; + SQInteger size = 0; + sq_getstringandsize(vm_, idx, &val, &size); + pBuffer->PutInt(size); + pBuffer->Put(val, size); + break; + } + case OT_TABLE: + { + pBuffer->PutInt(OT_TABLE); + if (writeState.CheckCache(pBuffer, obj._unVal.pTable)) + { + break; + } + sq_getdelegate(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + int count = sq_getsize(vm_, idx); + sq_push(vm_, idx); + sq_pushnull(vm_); + pBuffer->PutInt(count); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); + break; + } + case OT_ARRAY: + { + pBuffer->PutInt(OT_ARRAY); + if (writeState.CheckCache(pBuffer, obj._unVal.pArray)) + { + break; + } + int count = sq_getsize(vm_, idx); + pBuffer->PutInt(count); + sq_push(vm_, idx); + sq_pushnull(vm_); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); + break; + } + case OT_CLOSURE: + { + pBuffer->PutInt(OT_CLOSURE); + if (writeState.CheckCache(pBuffer, obj._unVal.pClosure)) + { + break; + } + + SQInteger nparams = 0, nfreevars = 0; + sq_getclosureinfo(vm_, idx, &nparams, &nfreevars); + if (nfreevars == 0 && _closure(obj)->_function->_defaultparams == 0) + { + pBuffer->PutChar(0); + + sq_push(vm_, idx); + if (SQ_FAILED(sq_writeclosure(vm_, closure_write, pBuffer))) + { + Error("Failed to write closure"); + } + sq_pop(vm_, 1); + } + else + { + // Unfortunately we can't use sq_writeclosure because it doesn't work well with + // outer variables + + pBuffer->PutChar(1); + + if (!_closure(obj)->Save(vm_, pBuffer, closure_write)) + { + Error("Failed to write closure\n"); + } + + int noutervalues = _closure(obj)->_function->_noutervalues; + for (int i = 0; i < noutervalues; ++i) + { + sq_pushobject(vm_, _closure(obj)->_outervalues[i]); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + } + + int ndefaultparams = _closure(obj)->_function->_ndefaultparams; + for (int i = 0; i < ndefaultparams; ++i) + { + sq_pushobject(vm_, _closure(obj)->_defaultparams[i]); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + } + } + + if (_closure(obj)->_env) + { + sq_pushobject(vm_, _closure(obj)->_env->_obj); + } + else + { + sq_pushnull(vm_); + } + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + + break; + } + case OT_NATIVECLOSURE: + { + pBuffer->PutInt(OT_NATIVECLOSURE); + sq_getclosurename(vm_, idx); + + const char* name = nullptr; + sq_getstring(vm_, -1, &name); + pBuffer->PutString(name); + + sq_pop(vm_, 1); + break; + } + case OT_CLASS: + { + pBuffer->PutInt(OT_CLASS); + if (writeState.CheckCache(pBuffer, obj._unVal.pClass)) + { + break; + } + SQUserPointer typetag = nullptr; + sq_gettypetag(vm_, idx, &typetag); + if (typetag == TYPETAG_VECTOR) + { + pBuffer->PutInt(VectorClassType); + } + else if (typetag != nullptr) + { + // Seems so dangerous to treat typetag as ScriptClassDesc_t* + // however we don't really have an option without some sort of tagged + // pointer. + pBuffer->PutInt(NativeClassType); + pBuffer->PutString(((ScriptClassDesc_t*)typetag)->m_pszScriptName); + } + else + { + // HACK: We can't easily identify when the type is a builtin to exclude + // so we just check against the only class we need to deal with at the moment + // which is "regexp" + const char* builtinName = nullptr; + if (_class(obj) == _class(regexpClass_)) + { + builtinName = "regexp"; + } + + if (builtinName) + { + pBuffer->PutInt(NativeClassType); + pBuffer->PutString(builtinName); + break; + } + + pBuffer->PutInt(ScriptClassType); + + sq_getbase(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + sq_push(vm_, idx); + sq_pushnull(vm_); + sq_getattributes(vm_, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + sq_pushnull(vm_); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + pBuffer->PutChar(1); + // TODO: Member Attributes + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + { + // HACK: Meta-methods are not included in an iterator of OT_CLASS + SQObjectPtrVec& metamethods = *(_ss(vm_)->_metamethods); + for (int i = 0; i < MT_LAST; ++i) + { + if (sq_type(_class(obj)->_metamethods[i]) != OT_NULL) + { + pBuffer->PutChar(1); + sq_pushobject(vm_, metamethods[i]); + sq_pushobject(vm_, _class(obj)->_metamethods[i]); + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + } + } + } + + pBuffer->PutChar(0); + } + break; + } + case OT_INSTANCE: + { + pBuffer->PutInt(OT_INSTANCE); + if (writeState.CheckCache(pBuffer, obj._unVal.pInstance)) + { + break; + } + sq_getclass(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + if (_instance(obj)->_class == _class(regexpClass_)) + { + sq_push(vm_, idx); + sq_pushstring(vm_, "pattern_", -1); + sq_rawget(vm_, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + break; + } + + { + // HACK: No way to get the default values part from accessing the class directly + SQUnsignedInteger nvalues = _instance(obj)->_class->_defaultvalues.size(); + for (SQUnsignedInteger n = 0; n < nvalues; n++) { + sq_pushobject(vm_, _instance(obj)->_values[n]); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + } + } + + SQUserPointer typetag; + sq_gettypetag(vm_, idx, &typetag); + + if (typetag == TYPETAG_VECTOR) + { + Vector* v = nullptr; + sq_getinstanceup(vm_, idx, (SQUserPointer*)&v, TYPETAG_VECTOR); + Assert(v); + pBuffer->PutFloat(v->x); + pBuffer->PutFloat(v->y); + pBuffer->PutFloat(v->z); + } + else if (typetag) + { + ClassInstanceData* pClassInstanceData; + sq_getinstanceup(vm_, idx, (SQUserPointer*)&pClassInstanceData, typetag); + + if (pClassInstanceData) + { + if (pClassInstanceData->desc->m_pszDescription[0] == SCRIPT_SINGLETON[0]) + { + // Do nothing, singleton should be created from just the class + } + else if (!pClassInstanceData->instanceId.IsEmpty()) + { + pBuffer->PutString(pClassInstanceData->instanceId); + + pBuffer->PutChar(pClassInstanceData->allowDestruct ? 1 : 0); + } + else + { + DevWarning("SquirrelVM::WriteObject: Unable to find instanceID for object of type %s, unable to serialize\n", + pClassInstanceData->desc->m_pszClassname); + pBuffer->PutString(""); + } + } + else + { + pBuffer->PutString(""); + } + } + + break; + } + case OT_WEAKREF: + { + pBuffer->PutInt(OT_WEAKREF); + sq_getweakrefval(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + break; + } + case OT_FUNCPROTO: //internal usage only + { + pBuffer->PutInt(OT_FUNCPROTO); + + if (writeState.CheckCache(pBuffer, obj._unVal.pFunctionProto)) + { + break; + } + + _funcproto(obj)->Save(vm_, pBuffer, closure_write); + } + case OT_OUTER: //internal usage only + { + pBuffer->PutInt(OT_OUTER); + + if (writeState.CheckCache(pBuffer, obj._unVal.pOuter)) + { + break; + } + + sq_pushobject(vm_, *_outer(obj)->_valptr); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + + break; + } + // case OT_USERDATA: + // case OT_GENERATOR: + // case OT_USERPOINTER: + // case OT_THREAD: + // + default: + Warning("SquirrelVM::WriteObject: Unexpected type %d", sq_gettype(vm_, idx)); + // Save a null instead + pBuffer->PutInt(OT_NULL); + } +} + +void SquirrelVM::WriteState(CUtlBuffer* pBuffer) +{ + SquirrelSafeCheck safeCheck(vm_); + + WriteStateMap writeState; + + sq_pushroottable(vm_); + + // Not really a check cache, but adds the root + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + writeState.CheckCache(pBuffer, _table(obj)); + + int count = sq_getsize(vm_, 1); + sq_pushnull(vm_); + pBuffer->PutInt(count); + + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); +} + +SQInteger closure_read(SQUserPointer file, SQUserPointer buf, SQInteger size) +{ + CUtlBuffer* pBuffer = (CUtlBuffer*)file; + pBuffer->Get(buf, size); + return pBuffer->IsValid() ? size : -1; +} + +void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) +{ + SquirrelSafeCheck safeCheck(vm_, 1); + + int thisType = pBuffer->GetInt(); + + switch (thisType) + { + case OT_NULL: + { + sq_pushnull(vm_); + break; + } + case OT_INTEGER: + { + sq_pushinteger(vm_, pBuffer->GetInt64()); + break; + } + case OT_FLOAT: + { + sq_pushfloat(vm_, pBuffer->GetFloat()); + break; + } + case OT_BOOL: + { + sq_pushbool(vm_, pBuffer->GetChar()); + break; + } + case OT_STRING: + { + int size = pBuffer->GetInt(); + char* buffer = new char[size + 1]; + pBuffer->Get(buffer, size); + buffer[size] = 0; + sq_pushstring(vm_, buffer, size); + delete[] buffer; + break; + } + case OT_TABLE: + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + ReadObject(pBuffer, readState); + + int count = pBuffer->GetInt(); + sq_newtableex(vm_, count); + readState.StoreTopInCache(marker); + + sq_push(vm_, -2); + sq_setdelegate(vm_, -2); + + sq_remove(vm_, -2); + + for (int i = 0; i < count; ++i) + { + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + sq_rawset(vm_, -3); + } + + break; + } + case OT_ARRAY: + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + int count = pBuffer->GetInt(); + sq_newarray(vm_, count); + readState.StoreTopInCache(marker); + + for (int i = 0; i < count; ++i) + { + sq_pushinteger(vm_, i); + ReadObject(pBuffer, readState); + sq_rawset(vm_, -3); + } + break; + } + case OT_CLOSURE: + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + if (pBuffer->GetChar() == 0) + { + if (SQ_FAILED(sq_readclosure(vm_, closure_read, pBuffer))) + { + Error("Failed to read closure\n"); + sq_pushnull(vm_); + break; + } + + readState.StoreTopInCache(marker); + } + else + { + SQObjectPtr ret; + if (!SQClosure::Load(vm_, pBuffer, closure_read, ret)) + { + Error("Failed to read closure\n"); + sq_pushnull(vm_); + break; + } + + vm_->Push(ret); + readState.StoreTopInCache(marker); + + int noutervalues = _closure(ret)->_function->_noutervalues; + for (int i = 0; i < noutervalues; ++i) + { + ReadObject(pBuffer, readState); + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + _closure(ret)->_outervalues[i] = obj; + sq_poptop(vm_); + } + + int ndefaultparams = _closure(ret)->_function->_ndefaultparams; + for (int i = 0; i < ndefaultparams; ++i) + { + ReadObject(pBuffer, readState); + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + _closure(ret)->_defaultparams[i] = obj; + sq_poptop(vm_); + } + } + + ReadObject(pBuffer, readState); + HSQOBJECT env; + sq_resetobject(&env); + sq_getstackobj(vm_, -1, &env); + if (!sq_isnull(env)) + { + HSQOBJECT obj; + sq_getstackobj(vm_, -2, &obj); + if (_closure(obj) == nullptr) + Warning("Closure is null\n"); + else + _closure(obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); + } + sq_poptop(vm_); + + break; + } + case OT_NATIVECLOSURE: + { + char closureName[128] = ""; + pBuffer->GetString(closureName, sizeof(closureName)); + + sq_pushroottable(vm_); + sq_pushstring(vm_, closureName, -1); + if (SQ_FAILED(sq_get(vm_, -2))) + { + Warning("SquirrelVM::ReadObject: Failed to find native closure\n"); + sq_pop(vm_, 1); + sq_pushnull(vm_); + } + sq_remove(vm_, -2); + + break; + } + case OT_CLASS: + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + ClassType classType = (ClassType)pBuffer->GetInt(); + + if (classType == VectorClassType) + { + sq_pushobject(vm_, vectorClass_); + readState.StoreTopInCache(marker); + } + else if (classType == NativeClassType) + { + char className[128] = ""; + pBuffer->GetString(className, sizeof(className)); + + sq_pushroottable(vm_); + sq_pushstring(vm_, className, -1); + if (SQ_FAILED(sq_get(vm_, -2))) + { + Warning("SquirrelVM::ReadObject: Failed to find native class: %s\n", className); + sq_pushnull(vm_); + } + sq_remove(vm_, -2); + readState.StoreTopInCache(marker); + } + else if (classType == ScriptClassType) + { + ReadObject(pBuffer, readState); + bool hasBase = sq_gettype(vm_, -1) != OT_NULL; + if (!hasBase) + { + sq_poptop(vm_); + } + + sq_newclass(vm_, hasBase); + readState.StoreTopInCache(marker); + + sq_pushnull(vm_); + ReadObject(pBuffer, readState); + sq_setattributes(vm_, -3); + sq_poptop(vm_); // Returns the old attributes + + while (pBuffer->GetChar()) + { + // TODO: Member Attributes + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + sq_newslot(vm_, -3, false); + } + } + else + { + Error("SquirrelVM::ReadObject: Unknown class type\n"); + sq_pushnull(vm_); + } + break; + } + case OT_INSTANCE: + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + ReadObject(pBuffer, readState); + + HSQOBJECT klass; + sq_resetobject(&klass); + sq_getstackobj(vm_, -1, &klass); + if (_class(klass) == _class(regexpClass_)) + { + sq_pushnull(vm_); + ReadObject(pBuffer, readState); + sq_call(vm_, 2, SQTrue, SQFalse); + + readState.StoreTopInCache(marker); + + sq_remove(vm_, -2); + + break; + } + + SQUserPointer typetag; + sq_gettypetag(vm_, -1, &typetag); + + if (typetag && typetag != TYPETAG_VECTOR && + ((ScriptClassDesc_t*)typetag)->m_pszDescription[0] == SCRIPT_SINGLETON[0]) + { + sq_poptop(vm_); + + Assert(sq_isclass(klass)); + + // singleton, lets find an equivlent in the root + bool foundSingleton = false; + sq_pushroottable(vm_); + sq_pushnull(vm_); + HSQOBJECT singleton; + sq_resetobject(&singleton); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + sq_getstackobj(vm_, -1, &singleton); + if (sq_isinstance(singleton) && _instance(singleton)->_class == _class(klass)) + { + foundSingleton = true; + + readState.StoreInCache(marker, singleton); + sq_addref(vm_, &singleton); + sq_pop(vm_, 2); + break; + } + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + if (!foundSingleton) + { + Warning("SquirrelVM::ReadObject: Failed to find singleton for %s\n", + ((ScriptClassDesc_t*)typetag)->m_pszScriptName); + } + + sq_pushobject(vm_, singleton); + break; + } + + + HSQOBJECT obj; + sq_createinstance(vm_, -1); + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + readState.StoreInCache(marker, obj); + + sq_remove(vm_, -2); + + { + // HACK: No way to get the default values part from accessing the class directly + SQUnsignedInteger nvalues = _instance(obj)->_class->_defaultvalues.size(); + for (SQUnsignedInteger n = 0; n < nvalues; n++) { + ReadObject(pBuffer, readState); + HSQOBJECT val; + sq_resetobject(&val); + sq_getstackobj(vm_, -1, &val); + _instance(obj)->_values[n] = val; + sq_pop(vm_, 1); + } + } + + if (typetag == TYPETAG_VECTOR) + { + float x = pBuffer->GetFloat(); + float y = pBuffer->GetFloat(); + float z = pBuffer->GetFloat(); + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) Vector(x, y, z); + } + else if (typetag) + { + ScriptClassDesc_t* pClassDesc = (ScriptClassDesc_t*)typetag; + + char instanceName[128] = ""; + pBuffer->GetString(instanceName, sizeof(instanceName)); + + HSQOBJECT* hinstance = new HSQOBJECT; + sq_resetobject(hinstance); + sq_getstackobj(vm_, -1, hinstance); + sq_addref(vm_, hinstance); + + if (*instanceName) + { + bool allowDestruct = (pBuffer->GetChar() == 1); + + auto instance = pClassDesc->pHelper->BindOnRead((HSCRIPT)hinstance, nullptr, instanceName); + if (instance == nullptr) + { + sq_release(vm_, hinstance); + delete hinstance; + sq_poptop(vm_); + sq_pushnull(vm_); + break; + } + + { + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) ClassInstanceData(instance, pClassDesc, instanceName, allowDestruct); + } + sq_setreleasehook(vm_, -1, allowDestruct ? &destructor_stub : &destructor_stub_instance); + } + else + { + sq_setinstanceup(vm_, -1, nullptr); + } + } + + break; + } + case OT_WEAKREF: + { + ReadObject(pBuffer, readState); + sq_weakref(vm_, -1); + break; + } + case OT_FUNCPROTO: //internal usage only + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + SQObjectPtr ret; + if (!SQFunctionProto::Load(vm_, pBuffer, closure_read, ret)) + { + Error("Failed to deserialize OT_FUNCPROTO\n"); + sq_pushnull(vm_); + break; + } + + vm_->Push(ret); + readState.StoreTopInCache(marker); + } + case OT_OUTER: //internal usage only + { + int marker = 0; + if (readState.CheckCache(pBuffer, vm_, &marker)) + { + break; + } + + SQOuter* outer = SQOuter::Create(_ss(vm_), nullptr); + vm_->Push(outer); + readState.StoreTopInCache(marker); + + ReadObject(pBuffer, readState); + HSQOBJECT inner; + sq_resetobject(&inner); + sq_getstackobj(vm_, -1, &inner); + outer->_value = inner; + outer->_valptr = &(outer->_value); + sq_poptop(vm_); + + break; + } + // case OT_USERDATA: + // case OT_GENERATOR: + // case OT_USERPOINTER: + // case OT_THREAD: + // + default: + Error("SquirrelVM::ReadObject: Unexpected type %d", thisType); + } +} + +void SquirrelVM::ReadState(CUtlBuffer* pBuffer) +{ + SquirrelSafeCheck safeCheck(vm_); + + ReadStateMap readState(vm_); + + sq_pushroottable(vm_); + + HSQOBJECT obj; + int marker = 0; + readState.CheckCache(pBuffer, vm_, &marker); + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + readState.StoreInCache(marker, obj); + + int count = pBuffer->GetInt(); + + for (int i = 0; i < count; ++i) + { + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + + sq_rawset(vm_, -3); + } + + sq_pop(vm_, 1); +} + +void SquirrelVM::RemoveOrphanInstances() +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Is this the right thing to do here? It's not really removing orphan instances + sq_collectgarbage(vm_); +} + +void SquirrelVM::DumpState() +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Dump state +} + +void SquirrelVM::SetOutputCallback(ScriptOutputFunc_t pFunc) +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Support output callbacks +} + +void SquirrelVM::SetErrorCallback(ScriptErrorFunc_t pFunc) +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Support error callbacks +} + +bool SquirrelVM::RaiseException(const char* pszExceptionText) +{ + SquirrelSafeCheck safeCheck(vm_); + sq_pushstring(vm_, pszExceptionText, -1); + sq_resetobject(&lastError_); + sq_getstackobj(vm_, -1, &lastError_); + sq_addref(vm_, &lastError_); + sq_pop(vm_, 1); + return true; +} + + +IScriptVM* makeSquirrelVM() +{ + return new SquirrelVM; +} diff --git a/vscript/vscript_squirrel.nut b/vscript/vscript_squirrel.nut new file mode 100644 index 000000000..904dbdcb3 --- /dev/null +++ b/vscript/vscript_squirrel.nut @@ -0,0 +1,572 @@ +static char g_Script_vscript_squirrel[] = R"vscript( +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Basic Squirrel implementation in VScript +// +//=============================================================================// + +Warning <- error; + +function clamp( val, min, max ) +{ + if ( max < min ) + return max; + if ( val < min ) + return min; + if ( val > max ) + return max; + return val; +} + +function max( a, b ) +{ + if ( a > b ) + return a; + return b; +} + +function min( a, b ) +{ + if ( a < b ) + return a; + return b; +} + +function RemapVal( val, A, B, C, D ) +{ + if ( A == B ) + { + if ( val >= B ) + return D; + return C; + }; + return C + (D - C) * (val - A) / (B - A); +} + +function RemapValClamped( val, A, B, C, D ) +{ + if ( A == B ) + { + if ( val >= B ) + return D; + return C; + }; + + local cVal = (val - A) / (B - A); + + if ( cVal <= 0.0 ) + return C; + + if ( cVal >= 1.0 ) + return D; + + return C + (D - C) * cVal; +} + +function Approach( target, value, speed ) +{ + local delta = target - value; + + if ( delta > speed ) + return value + speed; + if ( -speed > delta ) + return value - speed; + return target; +} + +function AngleDistance( next, cur ) +{ + local delta = next - cur + + if ( delta > 180.0 ) + return delta - 360.0; + if ( -180.0 > delta ) + return delta + 360.0; + 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"); +} + +class CSimpleCallChainer +{ + constructor(prefixString, scopeForThis, exactMatch) + { + prefix = prefixString; + scope = scopeForThis; + chain = []; + scope["Dispatch" + prefixString] <- Call.bindenv(this); + } + + function PostScriptExecute() + { + local func; + try { + func = scope[prefix]; + } catch(e) { + return; + } + if (typeof(func) != "function") + return; + chain.push(func); + } + + function Call() + { + foreach (func in chain) + { + func.pcall(scope); + } + } + + prefix = null; + scope = null; + chain = null; +} + +//--------------------------------------------------------- +// Hook handler +//--------------------------------------------------------- +local s_List = {} + +Hooks <- +{ + // table, string, closure, string + function Add( scope, event, callback, context ) + { + switch ( typeof scope ) + { + case "table": + case "instance": + case "class": + break; + default: + throw "invalid scope param"; + } + + if ( typeof event != "string" ) + throw "invalid event param"; + + if ( typeof callback != "function" ) + throw "invalid callback param"; + + if ( typeof context != "string" ) + throw "invalid context param"; + + if ( !(event in s_List) ) + s_List[event] <- {}; + + local t = s_List[event]; + + if ( !(scope in t) ) + t[scope] <- {}; + + t[scope][context] <- callback; + + return __UpdateHooks(); + } + + function Remove( event, context ) + { + local rem; + + if ( event ) + { + if ( event in s_List ) + { + foreach ( scope, ctx in s_List[event] ) + { + if ( context in ctx ) + { + delete ctx[context]; + } + + if ( !ctx.len() ) + { + if ( !rem ) + rem = []; + rem.append( event ); + rem.append( scope ); + } + } + } + } + else + { + foreach ( ev, t in s_List ) + { + foreach ( scope, ctx in t ) + { + if ( context in ctx ) + { + delete ctx[context]; + } + + if ( !ctx.len() ) + { + if ( !rem ) + rem = []; + rem.append( ev ); + rem.append( scope ); + } + } + } + } + + if ( rem ) + { + local c = rem.len() - 1; + for ( local i = 0; i < c; i += 2 ) + { + local ev = rem[i]; + local scope = rem[i+1]; + + if ( !s_List[ev][scope].len() ) + delete s_List[ev][scope]; + + if ( !s_List[ev].len() ) + delete s_List[ev]; + } + } + + return __UpdateHooks(); + } + + function Call( event, scope, ... ) + { + local firstReturn; + + if ( event in s_List ) + { + vargv.insert( 0, scope ); + + local t = s_List[event]; + if ( scope in t ) + { + foreach ( fn in t[scope] ) + { + //printf( "(%.4f) Calling hook %s:%s\n", Time(), event, context ); + + local r = fn.acall( vargv ); + if ( firstReturn == null ) + firstReturn = r; + } + } + else if ( !scope ) // global hook + { + foreach ( sc, ctx in t ) + { + vargv[0] = sc; + + foreach ( context, fn in ctx ) + { + //printf( "(%.4f) Calling hook (g) %s:%s\n", Time(), event, context ); + + local r = fn.acall( vargv ); + if ( firstReturn == null ) + firstReturn = r; + } + } + } + } + + return firstReturn; + } + + function __UpdateHooks() + { + return __UpdateScriptHooks( s_List ); + } +} + +//--------------------------------------------------------- +// Documentation +//--------------------------------------------------------- +__Documentation <- {} + +local developer = (delete developer)() + +if (developer) +{ + local DocumentedFuncs = {} + local DocumentedClasses = {} + local DocumentedEnums = {} + local DocumentedConsts = {} + local DocumentedHooks = {} + local DocumentedMembers = {} + + local function AddAliasedToTable(name, signature, description, table) + { + // This is an alias function, could use split() if we could guarantee + // that ':' would not occur elsewhere in the description and Squirrel had + // a convience join() function -- It has split() + local colon = description.find(":"); + if (colon == null) + colon = description.len(); + local alias = description.slice(1, colon); + description = description.slice(colon + 1); + name = alias; + signature = null; + + table[name] <- [signature, description]; + } + + function __Documentation::RegisterHelp(name, signature, description) + { + if (description.len() && description[0] == '#') + { + AddAliasedToTable(name, signature, description, DocumentedFuncs) + } + else + { + DocumentedFuncs[name] <- [signature, description]; + } + } + + function __Documentation::RegisterClassHelp(name, baseclass, description) + { + DocumentedClasses[name] <- [baseclass, description]; + } + + function __Documentation::RegisterEnumHelp(name, num_elements, description) + { + DocumentedEnums[name] <- [num_elements, description]; + } + + function __Documentation::RegisterConstHelp(name, signature, description) + { + if (description.len() && description[0] == '#') + { + AddAliasedToTable(name, signature, description, DocumentedConsts) + } + else + { + DocumentedConsts[name] <- [signature, description]; + } + } + + function __Documentation::RegisterHookHelp(name, signature, description) + { + DocumentedHooks[name] <- [signature, description]; + } + + function __Documentation::RegisterMemberHelp(name, signature, description) + { + DocumentedMembers[name] <- [signature, description]; + } + + local function printdoc( text ) + { + return ::printc(200,224,255,text); + } + + local function printdocl( text ) + { + return printdoc(text + "\n"); + } + + local function PrintClass(name, doc) + { + local text = "=====================================\n"; + text += ("Class: " + name + "\n"); + text += ("Base: " + doc[0] + "\n"); + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + text += "=====================================\n\n"; + + printdoc(text); + } + + local function PrintFunc(name, doc) + { + local text = "Function: " + name + "\n" + + if (doc[0] == null) + { + // Is an aliased function + text += ("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos().parameters) + { + if (k == 0 && v == "this") continue; + if (k > 1) text += (", "); + text += (v); + } + text += (")\n"); + } + else + { + text += ("Signature: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); + } + + local function PrintMember(name, doc) + { + local text = ("Member: " + name + "\n"); + text += ("Signature: " + doc[0] + "\n"); + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); + } + + local function PrintEnum(name, doc) + { + local text = "=====================================\n"; + text += ("Enum: " + name + "\n"); + text += ("Elements: " + doc[0] + "\n"); + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + text += "=====================================\n\n"; + + printdoc(text); + } + + local function PrintConst(name, doc) + { + local text = ("Constant: " + name + "\n"); + if (doc[0] == null) + { + text += ("Value: null\n"); + } + else + { + text += ("Value: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); + } + + local function PrintHook(name, doc) + { + local text = ("Hook: " + name + "\n"); + if (doc[0] == null) + { + // Is an aliased function + text += ("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos().parameters) + { + if (k == 0 && v == "this") continue; + if (k > 1) text += (", "); + text += (v); + } + text += (")\n"); + } + else + { + text += ("Signature: " + doc[0] + "\n"); + } + if (doc[1].len()) + text += ("Description: " + doc[1] + "\n"); + printdocl(text); + } + + local function PrintMatches( pattern, docs, printfunc ) + { + local matches = []; + local always = pattern == "*"; + + foreach( name, doc in docs ) + { + if (always || name.tolower().find(pattern) != null || (doc[1].len() && doc[1].tolower().find(pattern) != null)) + { + matches.append( name ); + } + } + + if ( !matches.len() ) + return 0; + + matches.sort(); + + foreach( name in matches ) + printfunc( name, docs[name] ); + + return 1; + } + + function __Documentation::PrintHelp(pattern = "*") + { + local patternLower = pattern.tolower(); + + // Have a specific order + if (!( + PrintMatches( patternLower, DocumentedEnums, PrintEnum ) | + PrintMatches( patternLower, DocumentedConsts, PrintConst ) | + PrintMatches( patternLower, DocumentedClasses, PrintClass ) | + PrintMatches( patternLower, DocumentedFuncs, PrintFunc ) | + PrintMatches( patternLower, DocumentedMembers, PrintMember ) | + PrintMatches( patternLower, DocumentedHooks, PrintHook ) + )) + { + printdocl("Pattern " + pattern + " not found"); + } + } +} +else +{ + __Documentation.RegisterHelp <- + __Documentation.RegisterClassHelp <- + __Documentation.RegisterEnumHelp <- + __Documentation.RegisterConstHelp <- + __Documentation.RegisterHookHelp <- + __Documentation.RegisterMemberHelp <- dummy + + function __Documentation::PrintHelp( pattern = null ) + { + printcl(200, 224, 255, "Documentation is not enabled. To enable documentation, restart the server with the 'developer' cvar set to 1 or higher."); + } +} + +if (developer) +{ + __Documentation.RegisterClassHelp( "Vector", "", "Basic 3-float Vector class." ); + __Documentation.RegisterHelp( "Vector::Length", "float Vector::Length()", "Return the vector's length." ); + __Documentation.RegisterHelp( "Vector::LengthSqr", "float Vector::LengthSqr()", "Return the vector's squared length." ); + __Documentation.RegisterHelp( "Vector::Length2D", "float Vector::Length2D()", "Return the vector's 2D length." ); + __Documentation.RegisterHelp( "Vector::Length2DSqr", "float Vector::Length2DSqr()", "Return the vector's squared 2D length." ); + + __Documentation.RegisterHelp( "Vector::Normalized", "float Vector::Normalized()", "Return a normalized version of the vector." ); + __Documentation.RegisterHelp( "Vector::Norm", "void Vector::Norm()", "Normalize the vector in place." ); + __Documentation.RegisterHelp( "Vector::Scale", "vector Vector::Scale(float)", "Scale the vector's magnitude and return the result." ); + __Documentation.RegisterHelp( "Vector::Dot", "float Vector::Dot(vector)", "Return the dot/scalar product of two vectors." ); + __Documentation.RegisterHelp( "Vector::Cross", "float Vector::Cross(vector)", "Return the vector product of two vectors." ); + + __Documentation.RegisterHelp( "Vector::ToKVString", "string Vector::ToKVString()", "Return a vector as a string in KeyValue form, without separation commas." ); + + __Documentation.RegisterMemberHelp( "Vector.x", "float Vector.x", "The vector's X coordinate on the cartesian X axis." ); + __Documentation.RegisterMemberHelp( "Vector.y", "float Vector.y", "The vector's Y coordinate on the cartesian Y axis." ); + __Documentation.RegisterMemberHelp( "Vector.z", "float Vector.z", "The vector's Z coordinate on the cartesian Z axis." ); + + __Documentation.RegisterHelp( "clamp", "float clamp(float, float, float)", "" ); + __Documentation.RegisterHelp( "max", "float max(float, float)", "" ); + __Documentation.RegisterHelp( "min", "float min(float, float)", "" ); + __Documentation.RegisterHelp( "RemapVal", "float RemapVal(float, float, float, float, float)", "" ); + __Documentation.RegisterHelp( "RemapValClamped", "float RemapValClamped(float, float, float, float, float)", "" ); + __Documentation.RegisterHelp( "Approach", "float Approach(float, float, float)", "" ); + __Documentation.RegisterHelp( "AngleDistance", "float AngleDistance(float, float)", "" ); + __Documentation.RegisterHelp( "FLerp", "float FLerp(float, float, float, float, float)", "" ); + __Documentation.RegisterHelp( "Lerp", "float Lerp(float, float, float)", "" ); + __Documentation.RegisterHelp( "SimpleSpline", "float SimpleSpline(float)", "" ); +} +)vscript"; \ No newline at end of file diff --git a/vscript/wscript b/vscript/wscript new file mode 100644 index 000000000..e56409dc5 --- /dev/null +++ b/vscript/wscript @@ -0,0 +1,43 @@ +#! /usr/bin/env python +# encoding: utf-8 + +from waflib import Utils +import os +import vpc_parser + +top = '.' +PROJECT_NAME = 'vscript' + +def options(opt): + return + +def configure(conf): + conf.load('squirrel') + conf.env.SQUIRRELDIR = './squirrel' + +def build(bld): + project = vpc_parser.parse_vpc( bld.env, 'vscript.vpc', '..' ) + + install_path = bld.env.PREFIX + if bld.env.DEST_OS != 'android': + install_path += '/vscript/bin' + + if bld.env.DEST_OS == 'win32': + libs += ['USER32'] + + includes = project["includes"] + source = project["sources"] + defines = project["defines"] + + bld.shlib( + source = source, + target = PROJECT_NAME, + name = PROJECT_NAME, + features = 'c cxx squirrel', + includes = includes, + defines = defines, + install_path = install_path, + subsystem = bld.env.MSVC_SUBSYSTEM, + idx = bld.get_taskgen_count() + ) + diff --git a/wscript b/wscript index 16f2263bd..84e617dbd 100644 --- a/wscript +++ b/wscript @@ -89,6 +89,7 @@ projects={ 'video', 'vphysics', 'vpklib', + 'vscript', 'vstdlib', 'vtf', 'utils/vtex', @@ -143,6 +144,7 @@ projects={ 'vgui2/vgui_controls', 'vphysics', 'vpklib', + 'vscript' 'vstdlib', 'vtf', 'stub_steam'