2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "eclass_fgd.h"
24 #include "debugging/debugging.h"
28 #include "ifilesystem.h"
29 #include "iscriplib.h"
30 #include "qerplugin.h"
31 #include "mainframe.h"
33 #include "string/string.h"
34 #include "eclasslib.h"
37 #include "stream/stringstream.h"
38 #include "moduleobservers.h"
40 #include "stream/textfilestream.h"
44 typedef std::map<const char*, EntityClass*, RawStringLessNoCase> EntityClasses;
45 EntityClasses g_EntityClassFGD_classes;
46 typedef std::map<const char*, EntityClass*, RawStringLess> BaseClasses;
47 BaseClasses g_EntityClassFGD_bases;
48 EntityClass *g_EntityClassFGD_bad = 0;
49 typedef std::map<CopiedString, ListAttributeType> ListAttributeTypes;
50 ListAttributeTypes g_listTypesFGD;
54 void EntityClassFGD_clear(){
55 for ( BaseClasses::iterator i = g_EntityClassFGD_bases.begin(); i != g_EntityClassFGD_bases.end(); ++i )
57 ( *i ).second->free( ( *i ).second );
59 g_EntityClassFGD_bases.clear();
60 g_listTypesFGD.clear();
63 EntityClass* EntityClassFGD_insertUniqueBase( EntityClass* entityClass ){
64 std::pair<BaseClasses::iterator, bool> result = g_EntityClassFGD_bases.insert( BaseClasses::value_type( entityClass->name(), entityClass ) );
65 if ( !result.second ) {
66 globalErrorStream() << "duplicate base class: " << makeQuoted( entityClass->name() ) << "\n";
67 //eclass_capture_state(entityClass);
68 //entityClass->free(entityClass);
70 return ( *result.first ).second;
73 EntityClass* EntityClassFGD_insertUnique( EntityClass* entityClass ){
74 EntityClassFGD_insertUniqueBase( entityClass );
75 std::pair<EntityClasses::iterator, bool> result = g_EntityClassFGD_classes.insert( EntityClasses::value_type( entityClass->name(), entityClass ) );
76 if ( !result.second ) {
77 globalErrorStream() << "duplicate entity class: " << makeQuoted( entityClass->name() ) << "\n";
78 eclass_capture_state( entityClass );
79 entityClass->free( entityClass );
81 return ( *result.first ).second;
84 void EntityClassFGD_forEach( EntityClassVisitor& visitor ){
85 for ( EntityClasses::iterator i = g_EntityClassFGD_classes.begin(); i != g_EntityClassFGD_classes.end(); ++i )
87 visitor.visit( ( *i ).second );
91 inline bool EntityClassFGD_parseToken( Tokeniser& tokeniser, const char* token ){
92 return string_equal( tokeniser.getToken(), token );
95 const char *PARSE_ERROR = "error parsing entity class definition";
97 void EntityClassFGD_parseSplitString( Tokeniser& tokeniser, CopiedString& string ){
98 StringOutputStream buffer( 256 );
101 buffer << tokeniser.getToken();
102 if ( !string_equal( tokeniser.getToken(), "+" ) ) {
103 tokeniser.ungetToken();
104 string = buffer.c_str();
110 void EntityClassFGD_parseClass( Tokeniser& tokeniser, bool fixedsize, bool isBase ){
111 EntityClass* entityClass = Eclass_Alloc();
112 entityClass->free = &Eclass_Free;
113 entityClass->fixedsize = fixedsize;
114 entityClass->inheritanceResolved = false;
115 entityClass->mins = Vector3( -8, -8, -8 );
116 entityClass->maxs = Vector3( 8, 8, 8 );
120 const char* property = tokeniser.getToken();
121 if ( string_equal( property, "=" ) ) {
124 else if ( string_equal( property, "base" ) ) {
125 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
128 const char* base = tokeniser.getToken();
129 if ( string_equal( base, ")" ) ) {
132 else if ( !string_equal( base, "," ) ) {
133 entityClass->m_parent.push_back( base );
137 else if ( string_equal( property, "size" ) ) {
138 entityClass->sizeSpecified = true;
139 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
140 Tokeniser_getFloat( tokeniser, entityClass->mins.x() );
141 Tokeniser_getFloat( tokeniser, entityClass->mins.y() );
142 Tokeniser_getFloat( tokeniser, entityClass->mins.z() );
143 const char* token = tokeniser.getToken();
144 if ( string_equal( token, "," ) ) {
145 Tokeniser_getFloat( tokeniser, entityClass->maxs.x() );
146 Tokeniser_getFloat( tokeniser, entityClass->maxs.y() );
147 Tokeniser_getFloat( tokeniser, entityClass->maxs.z() );
148 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
152 entityClass->maxs = entityClass->mins;
153 vector3_negate( entityClass->mins );
154 ASSERT_MESSAGE( string_equal( token, ")" ), "" );
157 else if ( string_equal( property, "color" ) ) {
158 entityClass->colorSpecified = true;
159 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
160 Tokeniser_getFloat( tokeniser, entityClass->color.x() );
161 entityClass->color.x() /= 256.0;
162 Tokeniser_getFloat( tokeniser, entityClass->color.y() );
163 entityClass->color.y() /= 256.0;
164 Tokeniser_getFloat( tokeniser, entityClass->color.z() );
165 entityClass->color.z() /= 256.0;
166 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
168 else if ( string_equal( property, "iconsprite" ) ) {
169 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
170 StringOutputStream buffer( 256 );
171 buffer << PathCleaned( tokeniser.getToken() );
172 entityClass->m_modelpath = buffer.c_str();
173 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
175 else if ( string_equal( property, "sprite" )
176 || string_equal( property, "decal" )
178 || string_equal( property, "overlay" )
179 || string_equal( property, "light" )
180 || string_equal( property, "keyframe" )
181 || string_equal( property, "animator" )
182 || string_equal( property, "quadbounds" ) ) {
183 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
184 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
187 else if ( string_equal( property, "sphere" )
188 || string_equal( property, "sweptplayerhull" )
189 || string_equal( property, "studio" )
190 || string_equal( property, "studioprop" )
191 || string_equal( property, "lightprop" )
192 || string_equal( property, "lightcone" )
193 || string_equal( property, "sidelist" ) ) {
194 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
195 if ( string_equal( tokeniser.getToken(), ")" ) ) {
196 tokeniser.ungetToken();
198 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
200 else if ( string_equal( property, "line" )
201 || string_equal( property, "cylinder" ) ) {
202 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
204 tokeniser.getToken();
206 tokeniser.getToken();
208 tokeniser.getToken();
211 if ( string_equal( tokeniser.getToken(), ")" ) ) {
212 tokeniser.ungetToken();
216 tokeniser.getToken();
218 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
220 else if ( string_equal( property, "wirebox" ) ) {
221 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
223 tokeniser.getToken();
224 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "," ), PARSE_ERROR );
226 tokeniser.getToken();
227 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
229 else if ( string_equal( property, "halfgridsnap" ) ) {
233 ERROR_MESSAGE( PARSE_ERROR );
237 entityClass->m_name = tokeniser.getToken();
240 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ":" ), PARSE_ERROR );
242 EntityClassFGD_parseSplitString( tokeniser, entityClass->m_comments );
245 tokeniser.nextLine();
247 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "[" ), PARSE_ERROR );
249 tokeniser.nextLine();
253 CopiedString key = tokeniser.getToken();
254 if ( string_equal( key.c_str(), "]" ) ) {
255 tokeniser.nextLine();
259 if ( string_equal_nocase( key.c_str(), "input" )
260 || string_equal_nocase( key.c_str(), "output" ) ) {
261 const char* name = tokeniser.getToken();
262 if ( !string_equal( name, "(" ) ) {
263 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
265 tokeniser.getToken();
266 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
267 const char* descriptionSeparator = tokeniser.getToken();
268 if ( string_equal( descriptionSeparator, ":" ) ) {
269 CopiedString description;
270 EntityClassFGD_parseSplitString( tokeniser, description );
274 tokeniser.ungetToken();
276 tokeniser.nextLine();
281 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
282 CopiedString type = tokeniser.getToken();
283 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
285 if ( string_equal_nocase( type.c_str(), "flags" ) ) {
286 EntityClassAttribute attribute;
288 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "=" ), PARSE_ERROR );
289 tokeniser.nextLine();
290 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "[" ), PARSE_ERROR );
291 tokeniser.nextLine();
294 const char* flag = tokeniser.getToken();
295 if ( string_equal( flag, "]" ) ) {
296 tokeniser.nextLine();
301 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ":" ), PARSE_ERROR );
303 tokeniser.getToken();
305 const char* defaultSeparator = tokeniser.getToken();
306 if ( string_equal( defaultSeparator, ":" ) ) {
307 tokeniser.getToken();
309 const char* descriptionSeparator = tokeniser.getToken();
310 if ( string_equal( descriptionSeparator, ":" ) ) {
311 EntityClassFGD_parseSplitString( tokeniser, attribute.m_description );
315 tokeniser.ungetToken();
321 tokeniser.ungetToken();
325 tokeniser.nextLine();
327 EntityClass_insertAttribute( *entityClass, key.c_str(), attribute );
329 else if ( string_equal_nocase( type.c_str(), "choices" ) ) {
330 EntityClassAttribute attribute;
332 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ":" ), PARSE_ERROR );
333 attribute.m_name = tokeniser.getToken();
334 const char* valueSeparator = tokeniser.getToken();
335 if ( string_equal( valueSeparator, ":" ) ) {
336 const char* value = tokeniser.getToken();
337 if ( !string_equal( value, ":" ) ) {
338 attribute.m_value = value;
342 tokeniser.ungetToken();
345 const char* descriptionSeparator = tokeniser.getToken();
346 if ( string_equal( descriptionSeparator, ":" ) ) {
347 EntityClassFGD_parseSplitString( tokeniser, attribute.m_description );
351 tokeniser.ungetToken();
357 tokeniser.ungetToken();
359 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "=" ), PARSE_ERROR );
360 tokeniser.nextLine();
361 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "[" ), PARSE_ERROR );
362 tokeniser.nextLine();
364 StringOutputStream listTypeName( 64 );
365 listTypeName << entityClass->m_name.c_str() << "_" << attribute.m_name.c_str();
366 attribute.m_type = listTypeName.c_str();
368 ListAttributeType& listType = g_listTypesFGD[listTypeName.c_str()];
372 const char* value = tokeniser.getToken();
373 if ( string_equal( value, "]" ) ) {
374 tokeniser.nextLine();
379 CopiedString tmp( value );
380 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ":" ), PARSE_ERROR );
381 const char* name = tokeniser.getToken();
382 listType.push_back( name, tmp.c_str() );
384 tokeniser.nextLine();
387 for ( ListAttributeType::const_iterator i = listType.begin(); i != listType.end(); ++i )
389 if ( string_equal( attribute.m_value.c_str(), ( *i ).first.c_str() ) ) {
390 attribute.m_value = ( *i ).second.c_str();
394 EntityClass_insertAttribute( *entityClass, key.c_str(), attribute );
396 else if ( string_equal_nocase( type.c_str(), "decal" ) ) {
398 else if ( string_equal_nocase( type.c_str(), "string" )
399 || string_equal_nocase( type.c_str(), "integer" )
400 || string_equal_nocase( type.c_str(), "studio" )
401 || string_equal_nocase( type.c_str(), "sprite" )
402 || string_equal_nocase( type.c_str(), "color255" )
403 || string_equal_nocase( type.c_str(), "target_source" )
404 || string_equal_nocase( type.c_str(), "target_destination" )
405 || string_equal_nocase( type.c_str(), "sound" )
407 || string_equal_nocase( type.c_str(), "angle" )
408 || string_equal_nocase( type.c_str(), "origin" )
409 || string_equal_nocase( type.c_str(), "float" )
410 || string_equal_nocase( type.c_str(), "node_dest" )
411 || string_equal_nocase( type.c_str(), "filterclass" )
412 || string_equal_nocase( type.c_str(), "vector" )
413 || string_equal_nocase( type.c_str(), "sidelist" )
414 || string_equal_nocase( type.c_str(), "material" )
415 || string_equal_nocase( type.c_str(), "vecline" )
416 || string_equal_nocase( type.c_str(), "axis" )
417 || string_equal_nocase( type.c_str(), "npcclass" )
418 || string_equal_nocase( type.c_str(), "target_name_or_class" )
419 || string_equal_nocase( type.c_str(), "pointentityclass" )
420 || string_equal_nocase( type.c_str(), "scene" ) ) {
421 if ( !string_equal( tokeniser.getToken(), "readonly" ) ) {
422 tokeniser.ungetToken();
425 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ":" ), PARSE_ERROR );
426 const char* attributeType = "string";
427 if ( string_equal_nocase( type.c_str(), "studio" ) ) {
428 attributeType = "model";
431 EntityClassAttribute attribute;
432 attribute.m_type = attributeType;
433 attribute.m_name = tokeniser.getToken();
435 const char* defaultSeparator = tokeniser.getToken();
436 if ( string_equal( defaultSeparator, ":" ) ) {
437 const char* value = tokeniser.getToken();
438 if ( !string_equal( value, ":" ) ) {
439 attribute.m_value = value;
443 tokeniser.ungetToken();
447 const char* descriptionSeparator = tokeniser.getToken();
448 if ( string_equal( descriptionSeparator, ":" ) ) {
449 EntityClassFGD_parseSplitString( tokeniser, attribute.m_description );
453 tokeniser.ungetToken();
459 tokeniser.ungetToken();
461 EntityClass_insertAttribute( *entityClass, key.c_str(), attribute );
465 ERROR_MESSAGE( "unknown key type: " << makeQuoted( type.c_str() ) );
467 tokeniser.nextLine();
471 EntityClassFGD_insertUniqueBase( entityClass );
475 EntityClassFGD_insertUnique( entityClass );
479 void EntityClassFGD_loadFile( const char* filename );
481 void EntityClassFGD_parse( TextInputStream& inputStream, const char* path ){
482 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( inputStream );
484 tokeniser.nextLine();
488 const char* blockType = tokeniser.getToken();
489 if ( blockType == 0 ) {
492 if ( string_equal( blockType, "@SolidClass" ) ) {
493 EntityClassFGD_parseClass( tokeniser, false, false );
495 else if ( string_equal( blockType, "@BaseClass" ) ) {
496 EntityClassFGD_parseClass( tokeniser, false, true );
498 else if ( string_equal( blockType, "@PointClass" )
500 || string_equal( blockType, "@KeyFrameClass" )
501 || string_equal( blockType, "@MoveClass" )
502 || string_equal( blockType, "@FilterClass" )
503 || string_equal( blockType, "@NPCClass" ) ) {
504 EntityClassFGD_parseClass( tokeniser, true, false );
507 else if ( string_equal( blockType, "@include" ) ) {
508 StringOutputStream includePath( 256 );
509 includePath << StringRange( path, path_get_filename_start( path ) );
510 includePath << tokeniser.getToken();
511 EntityClassFGD_loadFile( includePath.c_str() );
513 else if ( string_equal( blockType, "@mapsize" ) ) {
514 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
516 tokeniser.getToken();
517 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "," ), PARSE_ERROR );
519 tokeniser.getToken();
520 ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
524 ERROR_MESSAGE( "unknown block type: " << makeQuoted( blockType ) );
532 void EntityClassFGD_loadFile( const char* filename ){
533 TextFileInputStream file( filename );
534 if ( !file.failed() ) {
535 globalOutputStream() << "parsing entity classes from " << makeQuoted( filename ) << "\n";
537 EntityClassFGD_parse( file, filename );
541 EntityClass* EntityClassFGD_findOrInsert( const char *name, bool has_brushes ){
542 ASSERT_NOTNULL( name );
544 if ( string_empty( name ) ) {
545 return g_EntityClassFGD_bad;
548 EntityClasses::iterator i = g_EntityClassFGD_classes.find( name );
549 if ( i != g_EntityClassFGD_classes.end()
550 //&& string_equal((*i).first, name)
552 return ( *i ).second;
555 EntityClass* e = EntityClass_Create_Default( name, has_brushes );
556 return EntityClassFGD_insertUnique( e );
559 const ListAttributeType* EntityClassFGD_findListType( const char *name ){
560 ListAttributeTypes::iterator i = g_listTypesFGD.find( name );
561 if ( i != g_listTypesFGD.end() ) {
562 return &( *i ).second;
569 void EntityClassFGD_resolveInheritance( EntityClass* derivedClass ){
570 if ( derivedClass->inheritanceResolved == false ) {
571 derivedClass->inheritanceResolved = true;
572 for ( StringList::iterator j = derivedClass->m_parent.begin(); j != derivedClass->m_parent.end(); ++j )
574 BaseClasses::iterator i = g_EntityClassFGD_bases.find( ( *j ).c_str() );
575 if ( i == g_EntityClassFGD_bases.end() ) {
576 globalErrorStream() << "failed to find entityDef " << makeQuoted( ( *j ).c_str() ) << " inherited by " << makeQuoted( derivedClass->m_name.c_str() ) << "\n";
580 EntityClass* parentClass = ( *i ).second;
581 EntityClassFGD_resolveInheritance( parentClass );
582 if ( !derivedClass->colorSpecified ) {
583 derivedClass->colorSpecified = parentClass->colorSpecified;
584 derivedClass->color = parentClass->color;
586 if ( !derivedClass->sizeSpecified ) {
587 derivedClass->sizeSpecified = parentClass->sizeSpecified;
588 derivedClass->mins = parentClass->mins;
589 derivedClass->maxs = parentClass->maxs;
592 for ( EntityClassAttributes::iterator k = parentClass->m_attributes.begin(); k != parentClass->m_attributes.end(); ++k )
594 EntityClass_insertAttribute( *derivedClass, ( *k ).first.c_str(), ( *k ).second );
601 class EntityClassFGD : public ModuleObserver
603 std::size_t m_unrealised;
604 ModuleObservers m_observers;
606 EntityClassFGD() : m_unrealised( 3 ){
609 if ( --m_unrealised == 0 ) {
610 StringOutputStream filename( 256 );
611 filename << GlobalRadiant().getGameToolsPath() << GlobalRadiant().getGameName() << "/halflife.fgd";
612 EntityClassFGD_loadFile( filename.c_str() );
615 for ( EntityClasses::iterator i = g_EntityClassFGD_classes.begin(); i != g_EntityClassFGD_classes.end(); ++i )
617 EntityClassFGD_resolveInheritance( ( *i ).second );
618 if ( ( *i ).second->fixedsize && string_empty( ( *i ).second->m_modelpath.c_str() ) ) {
619 if ( !( *i ).second->sizeSpecified ) {
620 globalErrorStream() << "size not specified for entity class: " << makeQuoted( ( *i ).second->m_name.c_str() ) << '\n';
622 if ( !( *i ).second->colorSpecified ) {
623 globalErrorStream() << "color not specified for entity class: " << makeQuoted( ( *i ).second->m_name.c_str() ) << '\n';
629 for ( BaseClasses::iterator i = g_EntityClassFGD_bases.begin(); i != g_EntityClassFGD_bases.end(); ++i )
631 eclass_capture_state( ( *i ).second );
635 m_observers.realise();
639 if ( ++m_unrealised == 1 ) {
640 m_observers.unrealise();
641 EntityClassFGD_clear();
644 void attach( ModuleObserver& observer ){
645 m_observers.attach( observer );
647 void detach( ModuleObserver& observer ){
648 m_observers.detach( observer );
652 EntityClassFGD g_EntityClassFGD;
654 void EntityClassFGD_attach( ModuleObserver& observer ){
655 g_EntityClassFGD.attach( observer );
657 void EntityClassFGD_detach( ModuleObserver& observer ){
658 g_EntityClassFGD.detach( observer );
661 void EntityClassFGD_realise(){
662 g_EntityClassFGD.realise();
664 void EntityClassFGD_unrealise(){
665 g_EntityClassFGD.unrealise();
668 void EntityClassFGD_construct(){
669 // start by creating the default unknown eclass
670 g_EntityClassFGD_bad = EClass_Create( "UNKNOWN_CLASS", Vector3( 0.0f, 0.5f, 0.0f ), "" );
672 EntityClassFGD_realise();
675 void EntityClassFGD_destroy(){
676 EntityClassFGD_unrealise();
678 g_EntityClassFGD_bad->free( g_EntityClassFGD_bad );
681 class EntityClassFGDDependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef, public GlobalRadiantModuleRef
685 class EntityClassFGDAPI
687 EntityClassManager m_eclassmanager;
689 typedef EntityClassManager Type;
690 STRING_CONSTANT( Name, "halflife" );
693 EntityClassFGD_construct();
695 m_eclassmanager.findOrInsert = &EntityClassFGD_findOrInsert;
696 m_eclassmanager.findListType = &EntityClassFGD_findListType;
697 m_eclassmanager.forEach = &EntityClassFGD_forEach;
698 m_eclassmanager.attach = &EntityClassFGD_attach;
699 m_eclassmanager.detach = &EntityClassFGD_detach;
700 m_eclassmanager.realise = &EntityClassFGD_realise;
701 m_eclassmanager.unrealise = &EntityClassFGD_unrealise;
703 Radiant_attachGameToolsPathObserver( g_EntityClassFGD );
704 Radiant_attachGameNameObserver( g_EntityClassFGD );
706 ~EntityClassFGDAPI(){
707 Radiant_detachGameNameObserver( g_EntityClassFGD );
708 Radiant_detachGameToolsPathObserver( g_EntityClassFGD );
710 EntityClassFGD_destroy();
712 EntityClassManager* getTable(){
713 return &m_eclassmanager;
717 #include "modulesystem/singletonmodule.h"
718 #include "modulesystem/moduleregistry.h"
720 typedef SingletonModule<EntityClassFGDAPI, EntityClassFGDDependencies> EntityClassFGDModule;
721 typedef Static<EntityClassFGDModule> StaticEntityClassFGDModule;
722 StaticRegisterModule staticRegisterEntityClassFGD( StaticEntityClassFGDModule::instance() );