|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "fgdlib/GameData.h" // FGDLIB: eliminate dependency
#include "fgdlib/GDClass.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
GDclass::GDclass(void) { m_nVariables = 0; m_bBase = false; m_bSolid = false; m_bBase = false; m_bSolid = false; m_bModel = false; m_bMove = false; m_bKeyFrame = false; m_bPoint = false; m_bNPC = false; m_bFilter = false;
m_bHalfGridSnap = false;
m_bGotSize = false; m_bGotColor = false;
m_rgbColor.r = 220; m_rgbColor.g = 30; m_rgbColor.b = 220; m_rgbColor.a = 0;
m_pszDescription = NULL;
for (int i = 0; i < 3; i++) { m_bmins[i] = -8; m_bmaxs[i] = 8; } }
//-----------------------------------------------------------------------------
// Purpose: Destructor. Frees variable and helper lists.
//-----------------------------------------------------------------------------
GDclass::~GDclass(void) { //
// Free variables.
//
int nCount = m_Variables.Count(); for (int i = 0; i < nCount; i++) { GDinputvariable *pvi = m_Variables.Element(i); delete pvi; } m_Variables.RemoveAll();
//
// Free helpers.
//
nCount = m_Helpers.Count(); for (int i = 0; i < nCount; i++) { CHelperInfo *pHelper = m_Helpers.Element(i); delete pHelper; } m_Helpers.RemoveAll();
//
// Free inputs.
//
nCount = m_Inputs.Count(); for (int i = 0; i < nCount; i++) { CClassInput *pInput = m_Inputs.Element(i); delete pInput; } m_Inputs.RemoveAll();
//
// Free outputs.
//
nCount = m_Outputs.Count(); for (int i = 0; i < nCount; i++) { CClassOutput *pOutput = m_Outputs.Element(i); delete pOutput; } m_Outputs.RemoveAll();
delete m_pszDescription; }
//-----------------------------------------------------------------------------
// Purpose: Adds the base class's variables to our variable list. Acquires the
// base class's bounding box and color, if any.
// Input : pszBase - Name of base class to add.
//-----------------------------------------------------------------------------
void GDclass::AddBase(GDclass *pBase) { int iBaseIndex; Parent->ClassForName(pBase->GetName(), &iBaseIndex);
//
// Add variables from base - update variable table
//
for (int i = 0; i < pBase->GetVariableCount(); i++) { GDinputvariable *pVar = pBase->GetVariableAt(i); AddVariable(pVar, pBase, iBaseIndex, i); }
//
// Add inputs from the base.
// UNDONE: need to use references to inputs & outputs to conserve memory
//
int nCount = pBase->GetInputCount(); for (int i = 0; i < nCount; i++) { CClassInput *pInput = pBase->GetInput(i);
CClassInput *pNew = new CClassInput; *pNew = *pInput; AddInput(pNew); }
//
// Add outputs from the base.
//
nCount = pBase->GetOutputCount(); for (int i = 0; i < nCount; i++) { CClassOutput *pOutput = pBase->GetOutput(i);
CClassOutput *pNew = new CClassOutput; *pNew = *pOutput; AddOutput(pNew); }
//
// If we don't have a bounding box, try to get the base's box.
//
if (!m_bGotSize) { if (pBase->GetBoundBox(m_bmins, m_bmaxs)) { m_bGotSize = true; } }
//
// If we don't have a color, use the base's color.
//
if (!m_bGotColor) { m_rgbColor = pBase->GetColor(); m_bGotColor = true; } }
//-----------------------------------------------------------------------------
// Purpose: Adds the given GDInputVariable to this GDClass's list of variables.
// Input : pVar -
// pBase -
// iBaseIndex -
// iVarIndex -
// Output : Returns TRUE if the pVar pointer was copied directly into this GDClass,
// FALSE if not. If this function returns TRUE, pVar should not be
// deleted by the caller.
//-----------------------------------------------------------------------------
BOOL GDclass::AddVariable(GDinputvariable *pVar, GDclass *pBase, int iBaseIndex, int iVarIndex) { int iThisIndex; GDinputvariable *pThisVar = VarForName(pVar->GetName(), &iThisIndex); //
// Check to see if we are overriding an existing variable definition.
//
if (pThisVar != NULL) { //
// Same name, different type. Flag this as an error.
//
if (pThisVar->GetType() != pVar->GetType()) { return(false); }
GDinputvariable *pAddVar; bool bReturn;
//
// Check to see if we need to combine a choices/flags array.
//
if (pVar->GetType() == ivFlags || pVar->GetType() == ivChoices) { //
// Combine two variables' flags into a new variable. Add the new
// variable to the local variable list and modify the old variable's
// position in our variable map to reflect the new local variable.
// This way, we can have multiple inheritance.
//
GDinputvariable *pNewVar = new GDinputvariable;
*pNewVar = *pVar; pNewVar->Merge(*pThisVar);
pAddVar = pNewVar; bReturn = false; } else { pAddVar = pVar; bReturn = true; }
if (m_VariableMap[iThisIndex][0] == -1) { //
// "pThisVar" is a leaf variable - we can remove since it is overridden.
//
int nIndex = m_Variables.Find(pThisVar); Assert(nIndex != -1); delete pThisVar;
m_Variables.Element(nIndex) = pAddVar;
//
// No need to modify variable map - we just replaced
// the pointer in the local variable list.
//
} else { //
// "pThisVar" was declared in a base class - we can replace the reference in
// our variable map with the new variable.
//
m_VariableMap[iThisIndex][0] = iBaseIndex;
if (iBaseIndex == -1) { m_Variables.AddToTail(pAddVar); m_VariableMap[iThisIndex][1] = m_Variables.Count() - 1; } else { m_VariableMap[iThisIndex][1] = iVarIndex; } }
return(bReturn); } //
// New variable.
//
if (iBaseIndex == -1) { //
// Variable declared in the leaf class definition - add it to the list.
//
m_Variables.AddToTail(pVar); }
//
// Too many variables already declared in this class definition - abort.
//
if (m_nVariables == GD_MAX_VARIABLES) { //CUtlString str;
//str.Format("Too many gamedata variables for class \"%s\"", m_szName);
//AfxMessageBox(str);
return(false); }
//
// Add the variable to our list.
//
m_VariableMap[m_nVariables][0] = iBaseIndex; m_VariableMap[m_nVariables][1] = iVarIndex; ++m_nVariables; //
// We added the pointer to our list of items (see Variables.AddToTail, above) so
// we must return true here.
//
return(true); }
//-----------------------------------------------------------------------------
// Finds an input by name.
//-----------------------------------------------------------------------------
CClassInput *GDclass::FindInput(const char *szName) { int nCount = GetInputCount(); for (int i = 0; i < nCount; i++) { CClassInput *pInput = GetInput(i); if (!stricmp(pInput->GetName(), szName)) { return(pInput); } }
return(NULL); }
//-----------------------------------------------------------------------------
// Finds an output by name.
//-----------------------------------------------------------------------------
CClassOutput *GDclass::FindOutput(const char *szName) { int nCount = GetOutputCount(); for (int i = 0; i < nCount; i++) { CClassOutput *pOutput = GetOutput(i); if (!stricmp(pOutput->GetName(), szName)) { return(pOutput); } }
return(NULL); }
//-----------------------------------------------------------------------------
// Purpose: Gets the mins and maxs of the class's bounding box as read from the
// FGD file. This controls the onscreen representation of any entities
// derived from this class.
// Input : pfMins - Receives minimum X, Y, and Z coordinates for the class.
// pfMaxs - Receives maximum X, Y, and Z coordinates for the class.
// Output : Returns TRUE if this class has a specified bounding box, FALSE if not.
//-----------------------------------------------------------------------------
BOOL GDclass::GetBoundBox(Vector& pfMins, Vector& pfMaxs) { if (m_bGotSize) { pfMins[0] = m_bmins[0]; pfMins[1] = m_bmins[1]; pfMins[2] = m_bmins[2];
pfMaxs[0] = m_bmaxs[0]; pfMaxs[1] = m_bmaxs[1]; pfMaxs[2] = m_bmaxs[2]; }
return(m_bGotSize); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CHelperInfo *GDclass::GetHelper(int nIndex) { return m_Helpers.Element(nIndex); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CClassInput *GDclass::GetInput(int nIndex) { return m_Inputs.Element(nIndex); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CClassOutput *GDclass::GetOutput(int nIndex) { return m_Outputs.Element(nIndex); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : tr -
// pGD -
// Output : Returns TRUE if worth continuing, FALSE otherwise.
//-----------------------------------------------------------------------------
BOOL GDclass::InitFromTokens(TokenReader& tr, GameData *pGD) { Parent = pGD;
//
// Initialize VariableMap
//
for (int i = 0; i < GD_MAX_VARIABLES; i++) { m_VariableMap[i][0] = -1; m_VariableMap[i][1] = -1; }
//
// Parse all specifiers (base, size, color, etc.)
//
if (!ParseSpecifiers(tr)) { return(FALSE); }
//
// Specifiers should be followed by an "="
//
if (!GDSkipToken(tr, OPERATOR, "=")) { return(FALSE); }
//
// Parse the class name.
//
if (!GDGetToken(tr, m_szName, sizeof(m_szName), IDENT)) { return(FALSE); }
//
// Check next operator - if ":", we have a description - if "[",
// we have no description.
//
char szToken[MAX_TOKEN]; if ((tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR) && IsToken(szToken, ":")) { // Skip ":"
tr.NextToken(szToken, sizeof(szToken));
//
// Free any existing description and set the pointer to NULL so that GDGetToken
// allocates memory for us.
//
delete m_pszDescription; m_pszDescription = NULL;
// Load description
if (!GDGetTokenDynamic(tr, &m_pszDescription, STRING)) { return(FALSE); } }
//
// Opening square brace.
//
if (!GDSkipToken(tr, OPERATOR, "[")) { return(FALSE); }
//
// Get class variables.
//
if (!ParseVariables(tr)) { return(FALSE); }
//
// Closing square brace.
//
if (!GDSkipToken(tr, OPERATOR, "]")) { return(FALSE); }
return(TRUE); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseBase(TokenReader &tr) { char szToken[MAX_TOKEN];
while (1) { if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT)) { return(false); }
//
// Find base class in list of classes.
//
GDclass *pBase = Parent->ClassForName(szToken); if (pBase == NULL) { GDError(tr, "undefined base class '%s", szToken); return(false); }
AddBase(pBase);
if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR)) { return(false); }
if (IsToken(szToken, ")")) { break; } else if (!IsToken(szToken, ",")) { GDError(tr, "expecting ',' or ')', but found %s", szToken); return(false); } }
return(true); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseColor(TokenReader &tr) { char szToken[MAX_TOKEN];
//
// Red.
//
if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER)) { return(false); } BYTE r = atoi(szToken);
//
// Green.
//
if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER)) { return(false); } BYTE g = atoi(szToken);
//
// Blue.
//
if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER)) { return(false); } BYTE b = atoi(szToken);
m_rgbColor.r = r; m_rgbColor.g = g; m_rgbColor.b = b; m_rgbColor.a = 0;
m_bGotColor = true;
if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, ")")) { return(false); } return(true); }
//-----------------------------------------------------------------------------
// Purpose: Parses a helper from the FGD file. Helpers are of the following format:
//
// <helpername>(<parameter 0> <parameter 1> ... <parameter n>)
//
// When this function is called, the helper name has already been parsed.
// Input : tr - Tokenreader to use for parsing.
// pszHelperName - Name of the helper being declared.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseHelper(TokenReader &tr, char *pszHelperName) { char szToken[MAX_TOKEN];
CHelperInfo *pHelper = new CHelperInfo; pHelper->SetName(pszHelperName);
bool bCloseParen = false; while (!bCloseParen) { trtoken_t eType = tr.PeekTokenType(szToken,sizeof(szToken));
if (eType == OPERATOR) { if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR)) { delete pHelper; return(false); }
if (IsToken(szToken, ")")) { bCloseParen = true; } else if (IsToken(szToken, "=")) { delete pHelper; return(false); } } else { if (!GDGetToken(tr, szToken, sizeof(szToken), eType)) { delete pHelper; return(false); } else { pHelper->AddParameter(szToken); } } }
m_Helpers.AddToTail(pHelper);
return(true); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseSize(TokenReader &tr) { char szToken[MAX_TOKEN];
//
// Mins.
//
for (int i = 0; i < 3; i++) { if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER)) { return(false); }
m_bmins[i] = (float)atof(szToken); }
if (tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR && IsToken(szToken, ",")) { //
// Skip ","
//
tr.NextToken(szToken, sizeof(szToken));
//
// Get maxes.
//
for (int i = 0; i < 3; i++) { if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER)) { return(false); } m_bmaxs[i] = (float)atof(szToken); } } else { //
// Split mins across origin.
//
for (int i = 0; i < 3; i++) { float div2 = m_bmins[i] / 2; m_bmaxs[i] = div2; m_bmins[i] = -div2; } }
m_bGotSize = true;
if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, ")")) { return(false); }
return(true); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseSpecifiers(TokenReader &tr) { char szToken[MAX_TOKEN];
while (tr.PeekTokenType() == IDENT) { tr.NextToken(szToken, sizeof(szToken));
//
// Handle specifiers that don't have any parens after them.
//
if (IsToken(szToken, "halfgridsnap")) { m_bHalfGridSnap = true; } else { //
// Handle specifiers require parens after them.
//
if (!GDSkipToken(tr, OPERATOR, "(")) { return(false); }
if (IsToken(szToken, "base")) { if (!ParseBase(tr)) { return(false); } } else if (IsToken(szToken, "size")) { if (!ParseSize(tr)) { return(false); } } else if (IsToken(szToken, "color")) { if (!ParseColor(tr)) { return(false); } } else if (!ParseHelper(tr, szToken)) { return(false); } } }
return(true); }
//-----------------------------------------------------------------------------
// Purpose: Reads an input using a given token reader. If the input is
// read successfully, the input is added to this class. If not, a
// parsing failure is returned.
// Input : tr - Token reader to use for parsing.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseInput(TokenReader &tr) { char szToken[MAX_TOKEN];
if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT, "input")) { return(false); }
CClassInput *pInput = new CClassInput;
bool bReturn = ParseInputOutput(tr, pInput); if (bReturn) { AddInput(pInput); } else { delete pInput; }
return(bReturn); }
//-----------------------------------------------------------------------------
// Purpose: Reads an input or output using a given token reader.
// Input : tr - Token reader to use for parsing.
// pInputOutput - Input or output to fill out.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseInputOutput(TokenReader &tr, CClassInputOutputBase *pInputOutput) { char szToken[MAX_TOKEN];
//
// Read the name.
//
if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT)) { return(false); }
pInputOutput->SetName(szToken);
//
// Read the type.
//
if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, "(")) { return(false); }
if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT)) { return(false); }
InputOutputType_t eType = pInputOutput->SetType(szToken); if (eType == iotInvalid) { GDError(tr, "bad input/output type '%s'", szToken); return(false); }
if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, ")")) { return(false); }
//
// Check the next operator - if ':', we have a description.
//
if ((tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR) && (IsToken(szToken, ":"))) { //
// Skip the ":".
//
tr.NextToken(szToken, sizeof(szToken));
//
// Read the description.
//
char *pszDescription; if (!GDGetTokenDynamic(tr, &pszDescription, STRING)) { return(false); }
pInputOutput->SetDescription(pszDescription); }
return(true); }
//-----------------------------------------------------------------------------
// Purpose: Reads an output using a given token reader. If the output is
// read successfully, the output is added to this class. If not, a
// parsing failure is returned.
// Input : tr - Token reader to use for parsing.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseOutput(TokenReader &tr) { char szToken[MAX_TOKEN];
if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT, "output")) { return(false); }
CClassOutput *pOutput = new CClassOutput;
bool bReturn = ParseInputOutput(tr, pOutput); if (bReturn) { AddOutput(pOutput); } else { delete pOutput; }
return(bReturn); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool GDclass::ParseVariables(TokenReader &tr) { while (1) { char szToken[MAX_TOKEN];
if (tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR) { break; }
if (!stricmp(szToken, "input")) { if (!ParseInput(tr)) { return(false); }
continue; }
if (!stricmp(szToken, "output")) { if (!ParseOutput(tr)) { return(false); }
continue; }
if (!stricmp(szToken, "key")) { GDGetToken(tr, szToken, sizeof(szToken)); }
GDinputvariable * var = new GDinputvariable; if (!var->InitFromTokens(tr)) { delete var; return(false); }
int nDupIndex; GDinputvariable *pDupVar = VarForName(var->GetName(), &nDupIndex); // check for duplicate variable definitions
if (pDupVar) { // Same name, different type.
if (pDupVar->GetType() != var->GetType()) { char szError[_MAX_PATH];
sprintf(szError, "%s: Variable '%s' is multiply defined with different types.", GetName(), var->GetName()); GDError(tr, szError); } }
if (!AddVariable(var, this, -1, m_Variables.Count())) { delete var; } }
return(true); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : iIndex -
// Output : GDinputvariable *
//-----------------------------------------------------------------------------
GDinputvariable *GDclass::GetVariableAt(int iIndex) { if ( iIndex < 0 || iIndex >= m_nVariables ) return NULL;
if (m_VariableMap[iIndex][0] == -1) { return m_Variables.Element(m_VariableMap[iIndex][1]); }
// find var's owner
GDclass *pVarClass = Parent->GetClass(m_VariableMap[iIndex][0]);
// find var in pVarClass
return pVarClass->GetVariableAt(m_VariableMap[iIndex][1]); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
GDinputvariable *GDclass::VarForName(const char *pszName, int *piIndex) { for(int i = 0; i < GetVariableCount(); i++) { GDinputvariable *pVar = GetVariableAt(i); if(!strcmpi(pVar->GetName(), pszName)) { if(piIndex) piIndex[0] = i; return pVar; } }
return NULL; }
void GDclass::GetHelperForGDVar( GDinputvariable *pVar, CUtlVector<const char *> *pszHelperName ) { const char *pszName = pVar->GetName(); for( int i = 0; i < GetHelperCount(); i++ ) { CHelperInfo *pHelper = GetHelper( i ); int nParamCount = pHelper->GetParameterCount(); for ( int j = 0; j < nParamCount; j++ ) { if ( !strcmpi( pszName, pHelper->GetParameter( j ) ) ) { pszHelperName->AddToTail(pHelper->GetName()); } } } }
|