Adding New Decl Types

From The DarkMod Wiki
Jump to: navigation, search

This is a summary of Slinky's D3W tutorial.

  • In SDK/framework/declmanager.h, add a new type to the declType_t enumeration.
  • In idGameLocal::Init(), register your new decl type.
declManager->RegisterDeclType( "nameofdecl", DECL_ENUMTYPE, idDeclAllocator<yourDeclClass> );
The first argument is the keyword mappers can use to specify what kind of decl they're making. The second argument links it to the type you made in step 1, and the last argument is used to link it to the declClass you'll be making to parse and store your decl.
  • Also in idGameLocal::Init(), register the folders and file-types that will be searched for your decls.
declManager->RegisterDeclFolder( "foldername", ".file_extension", DECL_ENUMTYPE );
The first argument is the folder name to search, the second argument is the file extensions to search, and the last argument links it to the type you made in step 1.
  • Modify idGameLocal::CacheDictionaryMedia() to precache any instances of your declaration specified in the spawnargs of an entity.
  • Create your decl class, inheriting from idDecl.
  • Create the class declaration... at a minimum, it should probably look something like this:
class yourDeclClass : public idDecl
{
 public:
   yourDeclClass();
   ~yourDeclClass();

   virtual size_t       Size( void ) const;
   virtual const char * DefaultDefinition( void ) const;
   virtual void         FreeData( void );
   virtual bool         Parse( const char *text, const int textLength );

 protected:
   idStr someOfYourDeclData;
   int someMoreOfYourDeclData;
};
  • Define the class constructors/deconstructors. These can do whatever you like. You might want to consider calling FreeData() in the deconstructor.
  • Define Size(). All decl classes need this.
size_t yourDeclClass::Size() const
{
  return sizeof(yourDeclClass);
}
  • Define DefaultDefinition(). This just returns the default declaration that is used if the mapper writes one that your function can't parse.
const char *yourDeclClass::DefaultDefinition() const
{
  return "{\n}\n";
}
Be careful to ensure that the string returned is parsable by your Parse() function. Maybe Bad Stuff™ will happen if you don't.
  • Define FreeData(). Each time Parse() is called, FreeData() is first called to reset your decl's state. This way, the decl manager can use an instance of your decl class to keep on parsing definitions until it succeeds, without having to delete it and create a new instance. Example definition:
void yourDeclClass::FreeData()
{
   someOfYourDeclData = "";
   someMoreOfYourDeclData = 0;
}
  • Define Parse(). This is given an array of text, and its length, and is expected to parse it into data or give up. If true is returned, the decl manager will assume the parse was successful. If false is returned, the decl manager will assume the parse failed and a default definition was loaded. If you return false, you should probably call MakeDefault(), which will parse the string returned by DefaultDefinition().
bool yourClassDecl::Parse( const char *text, const int textLength )
{
   idLexer src;
   idToken token;

   src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
   src.SetFlags( DECL_LEXER_FLAGS );

   // Parse the string using the Lexer.
   // Note that we will be given a block of text like so:
   //   nameofdecl asset/name
   //   {
   //       information
   //   }
   // nameofdecl may be omitted if the decl manager is able to assume that the mapper
   // was creating a decl of our type. (this is commonly the case with materials)
   // We might also be able to parse information that comes after our decl, so we
   // should be sure to stop parsing once we reach our outer closing brace.

   if ( the parse failed )
   {
       src.Warning("Unable to parse the file.");
       MakeDefault();
       return false;
   }

   return true;
}