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"
43 typedef std::map<const char *, EntityClass *, RawStringLessNoCase> EntityClasses;
44 EntityClasses g_EntityClassFGD_classes;
45 typedef std::map<const char *, EntityClass *, RawStringLess> BaseClasses;
46 BaseClasses g_EntityClassFGD_bases;
47 EntityClass *g_EntityClassFGD_bad = 0;
48 typedef std::map<CopiedString, ListAttributeType> ListAttributeTypes;
49 ListAttributeTypes g_listTypesFGD;
53 void EntityClassFGD_clear()
55 for (BaseClasses::iterator i = g_EntityClassFGD_bases.begin(); i != g_EntityClassFGD_bases.end(); ++i) {
56 (*i).second->free((*i).second);
58 g_EntityClassFGD_bases.clear();
59 g_listTypesFGD.clear();
62 EntityClass *EntityClassFGD_insertUniqueBase(EntityClass *entityClass)
64 std::pair<BaseClasses::iterator, bool> result = g_EntityClassFGD_bases.insert(
65 BaseClasses::value_type(entityClass->name(), entityClass));
67 globalErrorStream() << "duplicate base class: " << makeQuoted(entityClass->name()) << "\n";
68 //eclass_capture_state(entityClass);
69 //entityClass->free(entityClass);
71 return (*result.first).second;
74 EntityClass *EntityClassFGD_insertUnique(EntityClass *entityClass)
76 EntityClassFGD_insertUniqueBase(entityClass);
77 std::pair<EntityClasses::iterator, bool> result = g_EntityClassFGD_classes.insert(
78 EntityClasses::value_type(entityClass->name(), entityClass));
80 globalErrorStream() << "duplicate entity class: " << makeQuoted(entityClass->name()) << "\n";
81 eclass_capture_state(entityClass);
82 entityClass->free(entityClass);
84 return (*result.first).second;
87 void EntityClassFGD_forEach(EntityClassVisitor &visitor)
89 for (EntityClasses::iterator i = g_EntityClassFGD_classes.begin(); i != g_EntityClassFGD_classes.end(); ++i) {
90 visitor.visit((*i).second);
94 inline bool EntityClassFGD_parseToken(Tokeniser &tokeniser, const char *token)
96 return string_equal(tokeniser.getToken(), token);
99 const char *PARSE_ERROR = "error parsing entity class definition";
101 void EntityClassFGD_parseSplitString(Tokeniser &tokeniser, CopiedString &string)
103 StringOutputStream buffer(256);
105 buffer << tokeniser.getToken();
106 if (!string_equal(tokeniser.getToken(), "+")) {
107 tokeniser.ungetToken();
108 string = buffer.c_str();
114 void EntityClassFGD_parseClass(Tokeniser &tokeniser, bool fixedsize, bool isBase)
116 EntityClass *entityClass = Eclass_Alloc();
117 entityClass->free = &Eclass_Free;
118 entityClass->fixedsize = fixedsize;
119 entityClass->inheritanceResolved = false;
120 entityClass->mins = Vector3(-8, -8, -8);
121 entityClass->maxs = Vector3(8, 8, 8);
124 const char *property = tokeniser.getToken();
125 if (string_equal(property, "=")) {
127 } else if (string_equal(property, "base")) {
128 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
130 const char *base = tokeniser.getToken();
131 if (string_equal(base, ")")) {
133 } else if (!string_equal(base, ",")) {
134 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);
150 entityClass->maxs = entityClass->mins;
151 vector3_negate(entityClass->mins);
152 ASSERT_MESSAGE(string_equal(token, ")"), "");
154 } else if (string_equal(property, "color")) {
155 entityClass->colorSpecified = true;
156 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
157 Tokeniser_getFloat(tokeniser, entityClass->color.x());
158 entityClass->color.x() /= 256.0;
159 Tokeniser_getFloat(tokeniser, entityClass->color.y());
160 entityClass->color.y() /= 256.0;
161 Tokeniser_getFloat(tokeniser, entityClass->color.z());
162 entityClass->color.z() /= 256.0;
163 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
164 } else if (string_equal(property, "iconsprite")) {
165 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
166 StringOutputStream buffer(256);
167 buffer << PathCleaned(tokeniser.getToken());
168 entityClass->m_modelpath = buffer.c_str();
169 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
170 } else if (string_equal(property, "sprite")
171 || string_equal(property, "decal")
173 || string_equal(property, "overlay")
174 || string_equal(property, "light")
175 || string_equal(property, "keyframe")
176 || string_equal(property, "animator")
177 || string_equal(property, "quadbounds")) {
178 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
179 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
182 else if (string_equal(property, "sphere")
183 || string_equal(property, "sweptplayerhull")
184 || string_equal(property, "studio")
185 || string_equal(property, "studioprop")
186 || string_equal(property, "lightprop")
187 || string_equal(property, "lightcone")
188 || string_equal(property, "sidelist")) {
189 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
190 if (string_equal(tokeniser.getToken(), ")")) {
191 tokeniser.ungetToken();
193 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
194 } else if (string_equal(property, "line")
195 || string_equal(property, "cylinder")) {
196 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
198 tokeniser.getToken();
200 tokeniser.getToken();
202 tokeniser.getToken();
204 if (string_equal(tokeniser.getToken(), ")")) {
205 tokeniser.ungetToken();
209 tokeniser.getToken();
211 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
212 } else if (string_equal(property, "wirebox")) {
213 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
215 tokeniser.getToken();
216 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ","), PARSE_ERROR);
218 tokeniser.getToken();
219 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
220 } else if (string_equal(property, "halfgridsnap")) {
222 ERROR_MESSAGE(PARSE_ERROR);
226 entityClass->m_name = tokeniser.getToken();
229 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
231 EntityClassFGD_parseSplitString(tokeniser, entityClass->m_comments);
234 tokeniser.nextLine();
236 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "["), PARSE_ERROR);
238 tokeniser.nextLine();
241 CopiedString key = tokeniser.getToken();
242 if (string_equal(key.c_str(), "]")) {
243 tokeniser.nextLine();
247 if (string_equal_nocase(key.c_str(), "input")
248 || string_equal_nocase(key.c_str(), "output")) {
249 const char *name = tokeniser.getToken();
250 if (!string_equal(name, "(")) {
251 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
253 tokeniser.getToken();
254 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
255 const char *descriptionSeparator = tokeniser.getToken();
256 if (string_equal(descriptionSeparator, ":")) {
257 CopiedString description;
258 EntityClassFGD_parseSplitString(tokeniser, description);
260 tokeniser.ungetToken();
262 tokeniser.nextLine();
267 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
268 CopiedString type = tokeniser.getToken();
269 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
271 if (string_equal_nocase(type.c_str(), "flags")) {
272 EntityClassAttribute attribute;
274 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "="), PARSE_ERROR);
275 tokeniser.nextLine();
276 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "["), PARSE_ERROR);
277 tokeniser.nextLine();
279 const char *flag = tokeniser.getToken();
280 if (string_equal(flag, "]")) {
281 tokeniser.nextLine();
284 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
286 tokeniser.getToken();
288 const char *defaultSeparator = tokeniser.getToken();
289 if (string_equal(defaultSeparator, ":")) {
290 tokeniser.getToken();
292 const char *descriptionSeparator = tokeniser.getToken();
293 if (string_equal(descriptionSeparator, ":")) {
294 EntityClassFGD_parseSplitString(tokeniser, attribute.m_description);
296 tokeniser.ungetToken();
300 tokeniser.ungetToken();
304 tokeniser.nextLine();
306 EntityClass_insertAttribute(*entityClass, key.c_str(), attribute);
307 } else if (string_equal_nocase(type.c_str(), "choices")) {
308 EntityClassAttribute attribute;
310 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
311 attribute.m_name = tokeniser.getToken();
312 const char *valueSeparator = tokeniser.getToken();
313 if (string_equal(valueSeparator, ":")) {
314 const char *value = tokeniser.getToken();
315 if (!string_equal(value, ":")) {
316 attribute.m_value = value;
318 tokeniser.ungetToken();
321 const char *descriptionSeparator = tokeniser.getToken();
322 if (string_equal(descriptionSeparator, ":")) {
323 EntityClassFGD_parseSplitString(tokeniser, attribute.m_description);
325 tokeniser.ungetToken();
329 tokeniser.ungetToken();
331 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "="), PARSE_ERROR);
332 tokeniser.nextLine();
333 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "["), PARSE_ERROR);
334 tokeniser.nextLine();
336 StringOutputStream listTypeName(64);
337 listTypeName << entityClass->m_name.c_str() << "_" << attribute.m_name.c_str();
338 attribute.m_type = listTypeName.c_str();
340 ListAttributeType &listType = g_listTypesFGD[listTypeName.c_str()];
343 const char *value = tokeniser.getToken();
344 if (string_equal(value, "]")) {
345 tokeniser.nextLine();
348 CopiedString tmp(value);
349 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
350 const char *name = tokeniser.getToken();
351 listType.push_back(name, tmp.c_str());
353 tokeniser.nextLine();
356 for (ListAttributeType::const_iterator i = listType.begin(); i != listType.end(); ++i) {
357 if (string_equal(attribute.m_value.c_str(), (*i).first.c_str())) {
358 attribute.m_value = (*i).second.c_str();
362 EntityClass_insertAttribute(*entityClass, key.c_str(), attribute);
363 } else if (string_equal_nocase(type.c_str(), "decal")) {
364 } else if (string_equal_nocase(type.c_str(), "string")
365 || string_equal_nocase(type.c_str(), "integer")
366 || string_equal_nocase(type.c_str(), "studio")
367 || string_equal_nocase(type.c_str(), "sprite")
368 || string_equal_nocase(type.c_str(), "color255")
369 || string_equal_nocase(type.c_str(), "target_source")
370 || string_equal_nocase(type.c_str(), "target_destination")
371 || string_equal_nocase(type.c_str(), "sound")
373 || string_equal_nocase(type.c_str(), "angle")
374 || string_equal_nocase(type.c_str(), "origin")
375 || string_equal_nocase(type.c_str(), "float")
376 || string_equal_nocase(type.c_str(), "node_dest")
377 || string_equal_nocase(type.c_str(), "filterclass")
378 || string_equal_nocase(type.c_str(), "vector")
379 || string_equal_nocase(type.c_str(), "sidelist")
380 || string_equal_nocase(type.c_str(), "material")
381 || string_equal_nocase(type.c_str(), "vecline")
382 || string_equal_nocase(type.c_str(), "axis")
383 || string_equal_nocase(type.c_str(), "npcclass")
384 || string_equal_nocase(type.c_str(), "target_name_or_class")
385 || string_equal_nocase(type.c_str(), "pointentityclass")
386 || string_equal_nocase(type.c_str(), "scene")) {
387 if (!string_equal(tokeniser.getToken(), "readonly")) {
388 tokeniser.ungetToken();
391 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ":"), PARSE_ERROR);
392 const char *attributeType = "string";
393 if (string_equal_nocase(type.c_str(), "studio")) {
394 attributeType = "model";
397 EntityClassAttribute attribute;
398 attribute.m_type = attributeType;
399 attribute.m_name = tokeniser.getToken();
401 const char *defaultSeparator = tokeniser.getToken();
402 if (string_equal(defaultSeparator, ":")) {
403 const char *value = tokeniser.getToken();
404 if (!string_equal(value, ":")) {
405 attribute.m_value = value;
407 tokeniser.ungetToken();
411 const char *descriptionSeparator = tokeniser.getToken();
412 if (string_equal(descriptionSeparator, ":")) {
413 EntityClassFGD_parseSplitString(tokeniser, attribute.m_description);
415 tokeniser.ungetToken();
419 tokeniser.ungetToken();
421 EntityClass_insertAttribute(*entityClass, key.c_str(), attribute);
423 ERROR_MESSAGE("unknown key type: " << makeQuoted(type.c_str()));
425 tokeniser.nextLine();
429 EntityClassFGD_insertUniqueBase(entityClass);
431 EntityClassFGD_insertUnique(entityClass);
435 void EntityClassFGD_loadFile(const char *filename);
437 void EntityClassFGD_parse(TextInputStream &inputStream, const char *path)
439 Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(inputStream);
441 tokeniser.nextLine();
444 const char *blockType = tokeniser.getToken();
445 if (blockType == 0) {
448 if (string_equal(blockType, "@SolidClass")) {
449 EntityClassFGD_parseClass(tokeniser, false, false);
450 } else if (string_equal(blockType, "@BaseClass")) {
451 EntityClassFGD_parseClass(tokeniser, false, true);
452 } else if (string_equal(blockType, "@PointClass")
454 || string_equal(blockType, "@KeyFrameClass")
455 || string_equal(blockType, "@MoveClass")
456 || string_equal(blockType, "@FilterClass")
457 || string_equal(blockType, "@NPCClass")) {
458 EntityClassFGD_parseClass(tokeniser, true, false);
461 else if (string_equal(blockType, "@include")) {
462 StringOutputStream includePath(256);
463 includePath << StringRange(path, path_get_filename_start(path));
464 includePath << tokeniser.getToken();
465 EntityClassFGD_loadFile(includePath.c_str());
466 } else if (string_equal(blockType, "@mapsize")) {
467 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, "("), PARSE_ERROR);
469 tokeniser.getToken();
470 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ","), PARSE_ERROR);
472 tokeniser.getToken();
473 ASSERT_MESSAGE(EntityClassFGD_parseToken(tokeniser, ")"), PARSE_ERROR);
475 ERROR_MESSAGE("unknown block type: " << makeQuoted(blockType));
483 void EntityClassFGD_loadFile(const char *filename)
485 TextFileInputStream file(filename);
486 if (!file.failed()) {
487 globalOutputStream() << "parsing entity classes from " << makeQuoted(filename) << "\n";
489 EntityClassFGD_parse(file, filename);
493 EntityClass *EntityClassFGD_findOrInsert(const char *name, bool has_brushes)
495 ASSERT_NOTNULL(name);
497 if (string_empty(name)) {
498 return g_EntityClassFGD_bad;
501 EntityClasses::iterator i = g_EntityClassFGD_classes.find(name);
502 if (i != g_EntityClassFGD_classes.end()
503 //&& string_equal((*i).first, name)
508 EntityClass *e = EntityClass_Create_Default(name, has_brushes);
509 return EntityClassFGD_insertUnique(e);
512 const ListAttributeType *EntityClassFGD_findListType(const char *name)
514 ListAttributeTypes::iterator i = g_listTypesFGD.find(name);
515 if (i != g_listTypesFGD.end()) {
523 void EntityClassFGD_resolveInheritance(EntityClass *derivedClass)
525 if (derivedClass->inheritanceResolved == false) {
526 derivedClass->inheritanceResolved = true;
527 for (StringList::iterator j = derivedClass->m_parent.begin(); j != derivedClass->m_parent.end(); ++j) {
528 BaseClasses::iterator i = g_EntityClassFGD_bases.find((*j).c_str());
529 if (i == g_EntityClassFGD_bases.end()) {
530 globalErrorStream() << "failed to find entityDef " << makeQuoted((*j).c_str()) << " inherited by "
531 << makeQuoted(derivedClass->m_name.c_str()) << "\n";
533 EntityClass *parentClass = (*i).second;
534 EntityClassFGD_resolveInheritance(parentClass);
535 if (!derivedClass->colorSpecified) {
536 derivedClass->colorSpecified = parentClass->colorSpecified;
537 derivedClass->color = parentClass->color;
539 if (!derivedClass->sizeSpecified) {
540 derivedClass->sizeSpecified = parentClass->sizeSpecified;
541 derivedClass->mins = parentClass->mins;
542 derivedClass->maxs = parentClass->maxs;
545 for (EntityClassAttributes::iterator k = parentClass->m_attributes.begin();
546 k != parentClass->m_attributes.end(); ++k) {
547 EntityClass_insertAttribute(*derivedClass, (*k).first.c_str(), (*k).second);
554 class EntityClassFGD : public ModuleObserver {
555 std::size_t m_unrealised;
556 ModuleObservers m_observers;
558 EntityClassFGD() : m_unrealised(3)
564 if (--m_unrealised == 0) {
565 StringOutputStream filename(256);
566 filename << GlobalRadiant().getGameToolsPath() << GlobalRadiant().getGameName() << "/halflife.fgd";
567 EntityClassFGD_loadFile(filename.c_str());
570 for (EntityClasses::iterator i = g_EntityClassFGD_classes.begin();
571 i != g_EntityClassFGD_classes.end(); ++i) {
572 EntityClassFGD_resolveInheritance((*i).second);
573 if ((*i).second->fixedsize && string_empty((*i).second->m_modelpath.c_str())) {
574 if (!(*i).second->sizeSpecified) {
575 globalErrorStream() << "size not specified for entity class: "
576 << makeQuoted((*i).second->m_name.c_str()) << '\n';
578 if (!(*i).second->colorSpecified) {
579 globalErrorStream() << "color not specified for entity class: "
580 << makeQuoted((*i).second->m_name.c_str()) << '\n';
586 for (BaseClasses::iterator i = g_EntityClassFGD_bases.begin(); i != g_EntityClassFGD_bases.end(); ++i) {
587 eclass_capture_state((*i).second);
591 m_observers.realise();
597 if (++m_unrealised == 1) {
598 m_observers.unrealise();
599 EntityClassFGD_clear();
603 void attach(ModuleObserver &observer)
605 m_observers.attach(observer);
608 void detach(ModuleObserver &observer)
610 m_observers.detach(observer);
614 EntityClassFGD g_EntityClassFGD;
616 void EntityClassFGD_attach(ModuleObserver &observer)
618 g_EntityClassFGD.attach(observer);
621 void EntityClassFGD_detach(ModuleObserver &observer)
623 g_EntityClassFGD.detach(observer);
626 void EntityClassFGD_realise()
628 g_EntityClassFGD.realise();
631 void EntityClassFGD_unrealise()
633 g_EntityClassFGD.unrealise();
636 void EntityClassFGD_construct()
638 // start by creating the default unknown eclass
639 g_EntityClassFGD_bad = EClass_Create("UNKNOWN_CLASS", Vector3(0.0f, 0.5f, 0.0f), "");
641 EntityClassFGD_realise();
644 void EntityClassFGD_destroy()
646 EntityClassFGD_unrealise();
648 g_EntityClassFGD_bad->free(g_EntityClassFGD_bad);
651 class EntityClassFGDDependencies
652 : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef, public GlobalRadiantModuleRef {
655 class EntityClassFGDAPI {
656 EntityClassManager m_eclassmanager;
658 typedef EntityClassManager Type;
660 STRING_CONSTANT(Name, "halflife");
664 EntityClassFGD_construct();
666 m_eclassmanager.findOrInsert = &EntityClassFGD_findOrInsert;
667 m_eclassmanager.findListType = &EntityClassFGD_findListType;
668 m_eclassmanager.forEach = &EntityClassFGD_forEach;
669 m_eclassmanager.attach = &EntityClassFGD_attach;
670 m_eclassmanager.detach = &EntityClassFGD_detach;
671 m_eclassmanager.realise = &EntityClassFGD_realise;
672 m_eclassmanager.unrealise = &EntityClassFGD_unrealise;
674 Radiant_attachGameToolsPathObserver(g_EntityClassFGD);
675 Radiant_attachGameNameObserver(g_EntityClassFGD);
680 Radiant_detachGameNameObserver(g_EntityClassFGD);
681 Radiant_detachGameToolsPathObserver(g_EntityClassFGD);
683 EntityClassFGD_destroy();
686 EntityClassManager *getTable()
688 return &m_eclassmanager;
692 #include "modulesystem/singletonmodule.h"
693 #include "modulesystem/moduleregistry.h"
695 typedef SingletonModule<EntityClassFGDAPI, EntityClassFGDDependencies> EntityClassFGDModule;
696 typedef Static<EntityClassFGDModule> StaticEntityClassFGDModule;
697 StaticRegisterModule staticRegisterEntityClassFGD(StaticEntityClassFGDModule::instance());