aboutsummaryrefslogtreecommitdiff
path: root/mp/src/fgdlib/gdclass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mp/src/fgdlib/gdclass.cpp')
-rw-r--r--mp/src/fgdlib/gdclass.cpp1041
1 files changed, 1041 insertions, 0 deletions
diff --git a/mp/src/fgdlib/gdclass.cpp b/mp/src/fgdlib/gdclass.cpp
new file mode 100644
index 00000000..be07610e
--- /dev/null
+++ b/mp/src/fgdlib/gdclass.cpp
@@ -0,0 +1,1041 @@
+//========= 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());
+ }
+ }
+ }
+}
+
+
+