diff options
Diffstat (limited to 'hammer/shell.cpp')
| -rw-r--r-- | hammer/shell.cpp | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/hammer/shell.cpp b/hammer/shell.cpp new file mode 100644 index 0000000..239efe5 --- /dev/null +++ b/hammer/shell.cpp @@ -0,0 +1,547 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Handles parsing and routing of shell commands to their handlers. +// +// $NoKeywords: $ +//=============================================================================// + +#include "stdafx.h" +#include "MainFrm.h" +#include "MapDoc.h" +#include "MapEntity.h" +#include "Shell.h" +#include "hammer.h" +#include "filesystem_helpers.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Shell command handler function pointer. +//----------------------------------------------------------------------------- +typedef bool (CShell::*ShellHandlerFunc_t)(const char *pszCommand, const char *pszArguments); + + +//----------------------------------------------------------------------------- +// Dispatch table entry. +//----------------------------------------------------------------------------- +struct ShellDispatchTable_t +{ + const char *pszCommand; // Name of command associated with this entry. + ShellHandlerFunc_t pfnHandler; // Function handler for the command. +}; + + +//----------------------------------------------------------------------------- +// Dispatch table for shell commands. +//----------------------------------------------------------------------------- +ShellDispatchTable_t CShell::m_DispatchTable[] = +{ + { "session_begin", &CShell::BeginSession }, + { "session_end", &CShell::EndSession }, + { "entity_create", &CShell::EntityCreate }, + { "entity_delete", &CShell::EntityDelete }, + { "entity_set_keyvalue", &CShell::EntitySetKeyValue }, + { "entity_rotate_incremental", &CShell::EntityRotateIncremental }, + + { "map_check_version", &CShell::CheckMapVersion }, + { "node_create", &CShell::NodeCreate }, + { "node_delete", &CShell::NodeDelete }, + { "nodelink_create", &CShell::NodeLinkCreate }, + { "nodelink_delete", &CShell::NodeLinkDelete }, + { "release_video_memory", &CShell::ReleaseVideoMemory }, + { "grab_video_memory", &CShell::GrabVideoMemory }, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CShell::CShell(void) +{ + m_pDoc = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. +//----------------------------------------------------------------------------- +CShell::~CShell(void) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Initiates a shell editing session. +// Input : pszCommand - Should be "session_begin". +// pszArguments - Filename and file version in the engine. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::BeginSession(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && !m_pDoc->IsShellSessionActive()) + { + if (DoVersionCheck(pszArguments)) + { + m_pDoc->BeginShellSession(); + return(true); + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Verifies that the map begine edited in the engine is the same name +// and version as the active document. This prevents problems with +// editing out of sync versions of the map via the engine. +// Input : pszCommand - Should be "map_check_version". +// pszArguments - Filename and file version in the engine. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::CheckMapVersion(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + return(DoVersionCheck(pszArguments)); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Verifies that the map being edited in the engine is the same name +// and version as the active document. This prevents problems with +// editing out of sync versions of the map via the engine. +// Input : pszCommand - +// pszArguments - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::DoVersionCheck(const char *pszArguments) +{ + if (m_pDoc != NULL) + { + char szEngineMapPath[MAX_PATH]; + int nEngineMapVersion; + + if (sscanf(pszArguments, "%s %d", szEngineMapPath, &nEngineMapVersion) == 2) + { + char szEngineMapName[MAX_PATH]; + _splitpath(szEngineMapPath, NULL, NULL, szEngineMapName, NULL); + + char szDocName[MAX_PATH]; + _splitpath(m_pDoc->GetPathName(), NULL, NULL, szDocName, NULL); + + int nDocVersion = m_pDoc->GetDocVersion(); + + if (!stricmp(szDocName, szEngineMapName) && (nDocVersion == nEngineMapVersion)) + { + return(true); + } + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Verifies that the map begine edited in the engine is the same name +// and version as the active document. This prevents problems with +// editing out of sync versions of the map via the engine. +// Input : pszCommand - Should be "session_end". +// pszArguments - Filename and file version in the engine. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::EndSession(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + m_pDoc->EndShellSession(); + return(true); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates an entity of a given class at a specified location. +// Input : pszCommand - Should be "entity_create". +// pszArguments - Class name of entity and x, y, z coordinate at which +// to create it. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::EntityCreate(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + float x; + float y; + float z; + char szClassName[MAX_PATH]; + + if (sscanf(pszArguments, "%s %f %f %f", szClassName, &x, &y, &z) == 4) + { + bool bCreated = (m_pDoc->CreateEntity(szClassName, x, y, z) != NULL); + return(bCreated); + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Deletes an entity by class name and origin. +// Input : pszCommand - Should be "entity_delete". +// pszArguments - Class name of entity and x, y, z coordinates. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::EntityDelete(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + float x; + float y; + float z; + char szClassName[MAX_PATH]; + + if (sscanf(pszArguments, "%s %f %f %f", szClassName, &x, &y, &z) == 4) + { + bool bDeleted = m_pDoc->DeleteEntity(szClassName, x, y, z); + return(bDeleted); + } + } + + return(false); +} + +static void RotateMapEntity( CMapEntity *pEntity, const QAngle &rotation ) +{ + Vector origin; + pEntity->GetOrigin( origin ); + QAngle hammerRotate; + hammerRotate.Init( rotation.z, -rotation.x, rotation.y ); + pEntity->TransRotate( origin, hammerRotate ); +} + +bool CShell::EntityRotateIncremental(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + const int NUM_ROTATE_INCREMENTAL_ARGS = 7; + float x; + float y; + float z; + QAngle rotation; + char szArgs[NUM_ROTATE_INCREMENTAL_ARGS][512]; // classname, x, y, z, ax, ay, az + char token[1024]; + const char *pBuffer = pszArguments; + + int arg = 0; + while ( pBuffer && arg < NUM_ROTATE_INCREMENTAL_ARGS ) + { + pBuffer = ParseFile( pBuffer, token, NULL ); + if ( pBuffer ) + { + Q_strncpy( szArgs[arg], token, ARRAYSIZE(szArgs[arg]) ); + arg++; + } + } + if ( arg == NUM_ROTATE_INCREMENTAL_ARGS ) + { + x = atof(szArgs[1]); + y = atof(szArgs[2]); + z = atof(szArgs[3]); + CMapEntity *pEntity = m_pDoc->FindEntity(szArgs[0], x, y, z); + if (pEntity != NULL) + { + rotation.x = atof(szArgs[4]); + rotation.y = atof(szArgs[5]); + rotation.z = atof(szArgs[6]); + RotateMapEntity( pEntity, rotation ); + return true; + } + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a keyvalue on an entity, searching by classname & origin +// Input : pszCommand - Should be "entity_delete". +// pszArguments - Class name of entity and x, y, z coordinates. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::EntitySetKeyValue(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + const int NUM_KEY_VALUE_ARGS = 6; + float x; + float y; + float z; + char szArgs[NUM_KEY_VALUE_ARGS][512]; // classname, x, y, z, key, value + char token[1024]; + const char *pBuffer = pszArguments; + + int arg = 0; + while ( pBuffer && arg < NUM_KEY_VALUE_ARGS ) + { + pBuffer = ParseFile( pBuffer, token, NULL ); + if ( pBuffer ) + { + Q_strncpy( szArgs[arg], token, ARRAYSIZE(szArgs[arg]) ); + arg++; + } + } + if ( arg == NUM_KEY_VALUE_ARGS ) + { + x = atof(szArgs[1]); + y = atof(szArgs[2]); + z = atof(szArgs[3]); + CMapEntity *pEntity = m_pDoc->FindEntity(szArgs[0], x, y, z); + if (pEntity != NULL) + { + if ( !Q_stricmp( szArgs[4], "origin" ) ) + { + Vector origin; + sscanf(szArgs[5], "%f %f %f", &origin[0], &origin[1], &origin[2]); + Vector oldOrigin; + pEntity->GetOrigin( oldOrigin ); + pEntity->TransMove(origin - oldOrigin); + } + else if ( pEntity->IsSolidClass() && !Q_stricmp( szArgs[4], "angles" ) ) + { + QAngle angles; + sscanf(szArgs[5], "%f %f %f", &angles[0], &angles[1], &angles[2]); + + // build a relative transform from the previous state to the current state + // NOTE: This only works once since solid classes destructively modify transform info (GetAngles always returns identity) + // NOTE: Use rotateIncremental instead! + QAngle oldAngles; + pEntity->GetAngles( oldAngles ); + if ( oldAngles != angles ) + { + QAngle xformAngles; + RotationDelta( oldAngles, angles, &xformAngles ); + RotateMapEntity( pEntity, xformAngles ); + } + } + else + { + pEntity->SetKeyValue( szArgs[4], szArgs[5] ); + } + return true; + } + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates a navigation node of a given class at a specified location. +// Input : pszCommand - Should be "node_create". +// pszArguments - Class name of node to create, ID to assign it, and +// x, y, z coordinate at which to create the node. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::NodeCreate(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + float x; + float y; + float z; + int nID; + char szClassName[MAX_PATH]; + + if (sscanf(pszArguments, "%s %d %f %f %f", szClassName, &nID, &x, &y, &z) == 5) + { + m_pDoc->SetNextNodeID(nID); + m_pDoc->CreateEntity(szClassName, x, y, z); + + return(true); + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Deletes a navigation node by ID. +// Input : pszCommand - Should be "node_delete". +// pszArguments - Unique node ID of node to delete. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::NodeDelete(const char *pszCommand, const char *pszArguments) +{ + bool bFound = false; + + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + char szID[80]; + if (sscanf(pszArguments, "%s", szID) == 1) + { + CMapEntityList Found; + if (m_pDoc->FindEntitiesByKeyValue(Found, "nodeid", szID, false)) + { + FOR_EACH_OBJ( Found, pos ) + { + CMapEntity *pEntity = Found.Element(pos); + m_pDoc->DeleteObject(pEntity); + bFound = true; + } + } + } + } + + return(bFound); +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates a navigation node of a given class at a specified location. +// Input : pszCommand - Should be "nodelink_create". +// pszArguments - Node ids of start and end nodes, space delimited. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::NodeLinkCreate(const char *pszCommand, const char *pszArguments) +{ + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + char szIDStart[80]; + char szIDEnd[80]; + + if (sscanf(pszArguments, "%s %s", szIDStart, szIDEnd) == 2) + { + // + // It doesn't matter where we place it because it will move to the midpoint of the + // start and end entities. + // + CMapEntity *pEntity = m_pDoc->CreateEntity("info_node_link", 0, 0, 0); + if (pEntity != NULL) + { + pEntity->SetKeyValue("startnode", szIDStart); + pEntity->SetKeyValue("endnode", szIDEnd); + + return(true); + } + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Deletes a navigation node by class name and ID. +// Input : pszCommand - Should be "node_delete". +// pszArguments - Class name of node and unique node ID. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::NodeLinkDelete(const char *pszCommand, const char *pszArguments) +{ + bool bFound = false; + + if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive()) + { + char szIDStart[80]; + char szIDEnd[80]; + + if (sscanf(pszArguments, "%s %s", szIDStart, szIDEnd) == 2) + { + // + // Look for info_node_link entities with the appropriate start/end keys. + // + CMapEntityList Found; + if (m_pDoc->FindEntitiesByClassName(Found, "info_node_link", false)) + { + FOR_EACH_OBJ( Found, pos ) + { + CMapEntity *pEntity = Found.Element(pos); + + const char *pszNode1 = pEntity->GetKeyValue("startnode"); + const char *pszNode2 = pEntity->GetKeyValue("endnode"); + if ((pszNode1 != NULL) && (pszNode2 != NULL)) + { + if (((!stricmp(pszNode1, szIDStart)) && (!stricmp(pszNode2, szIDEnd))) || + ((!stricmp(pszNode1, szIDEnd)) && (!stricmp(pszNode2, szIDStart)))) + { + m_pDoc->DeleteObject(pEntity); + bFound = true; + } + } + } + } + } + } + + return(bFound); +} + + +//----------------------------------------------------------------------------- +// Purpose: Releases all video memory +// Input : pszCommand - Should be "release_video_memory". +// pszArguments - None. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::ReleaseVideoMemory(const char *pszCommand, const char *pszArguments) +{ + APP()->ReleaseVideoMemory(); + APP()->SuppressVideoAllocation(true); + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: Indicates it's safe to grab video memory +// Input : pszCommand - Should be "grab_video_memory". +// pszArguments - None. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::GrabVideoMemory(const char *pszCommand, const char *pszArguments) +{ + APP()->SuppressVideoAllocation(false); + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: Attempts to fund a command in the dispatch table, then routes the +// command and its arguments to the handler, if found. +// Input : pszCommand - Command and arguments. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CShell::RunCommand(const char *pszCommand) +{ + for (int nCommand = 0; nCommand < sizeof(m_DispatchTable) / sizeof(m_DispatchTable[0]); nCommand++) + { + int nCommandLen = strlen(m_DispatchTable[nCommand].pszCommand); + + if (!_strnicmp(pszCommand, m_DispatchTable[nCommand].pszCommand, nCommandLen)) + { + return((this->*m_DispatchTable[nCommand].pfnHandler)(m_DispatchTable[nCommand].pszCommand, &pszCommand[nCommandLen])); + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the map document that this shell should operate on. +// Input : pDoc - Pointer to document. +//----------------------------------------------------------------------------- +void CShell::SetDocument(CMapDoc *pDoc) +{ + m_pDoc = pDoc; +} + |