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_doom3.h"
24 #include "debugging/debugging.h"
28 #include "ifilesystem.h"
29 #include "iscriplib.h"
31 #include "qerplugin.h"
33 #include "generic/callback.h"
34 #include "string/string.h"
35 #include "eclasslib.h"
38 #include "stream/stringstream.h"
39 #include "moduleobservers.h"
46 RawString(const char* value) : m_value(value)
49 const char* c_str() const
55 inline bool operator<(const RawString& self, const RawString& other)
57 return string_less_nocase(self.c_str(), other.c_str());
60 typedef std::map<RawString, EntityClass*> EntityClasses;
61 EntityClasses g_EntityClassDoom3_classes;
62 EntityClass *g_EntityClassDoom3_bad = 0;
65 void EntityClassDoom3_clear()
67 for(EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i)
69 (*i).second->free((*i).second);
71 g_EntityClassDoom3_classes.clear();
74 // entityClass will be inserted only if another of the same name does not already exist.
75 // if entityClass was inserted, the same object is returned, otherwise the already-existing object is returned.
76 EntityClass* EntityClassDoom3_insertUnique(EntityClass* entityClass)
78 return (*g_EntityClassDoom3_classes.insert(EntityClasses::value_type(entityClass->name(), entityClass)).first).second;
81 void EntityClassDoom3_forEach(EntityClassVisitor& visitor)
83 for(EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i)
85 visitor.visit((*i).second);
89 inline void printParseError(const char* message)
91 globalErrorStream() << message;
94 #define PARSE_RETURN_FALSE_IF_FAIL(expression) if(!(expression)) { printParseError(FILE_LINE "\nparse failed: " #expression "\n"); return false; } else
96 bool EntityClassDoom3_parseToken(Tokeniser& tokeniser)
98 const char* token = tokeniser.getToken();
99 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
103 bool EntityClassDoom3_parseToken(Tokeniser& tokeniser, const char* string)
105 const char* token = tokeniser.getToken();
106 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
107 return string_equal(token, string);
110 bool EntityClassDoom3_parseString(Tokeniser& tokeniser, const char*& s)
112 const char* token = tokeniser.getToken();
113 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
118 bool EntityClassDoom3_parseString(Tokeniser& tokeniser, CopiedString& s)
120 const char* token = tokeniser.getToken();
121 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
126 bool EntityClassDoom3_parseString(Tokeniser& tokeniser, StringOutputStream& s)
128 const char* token = tokeniser.getToken();
129 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
134 bool EntityClassDoom3_parseUnknown(Tokeniser& tokeniser)
137 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
139 //globalOutputStream() << "parsing unknown block " << makeQuoted(name) << "\n";
141 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
142 tokeniser.nextLine();
144 std::size_t depth = 1;
148 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
149 if(string_equal(token, "}"))
153 tokeniser.nextLine();
157 else if(string_equal(token, "{"))
161 tokeniser.nextLine();
173 CopiedString m_parent;
174 typedef std::map<CopiedString, CopiedString> Anims;
176 Model() : m_resolved(false)
181 typedef std::map<CopiedString, Model> Models;
185 void Model_resolveInheritance(const char* name, Model& model)
187 if(model.m_resolved == false)
189 model.m_resolved = true;
191 if(!string_empty(model.m_parent.c_str()))
193 Models::iterator i = g_models.find(model.m_parent);
194 if(i == g_models.end())
196 globalErrorStream() << "model " << name << " inherits unknown model " << model.m_parent.c_str() << "\n";
200 Model_resolveInheritance((*i).first.c_str(), (*i).second);
201 model.m_mesh = (*i).second.m_mesh;
202 model.m_skin = (*i).second.m_skin;
208 bool EntityClassDoom3_parseModel(Tokeniser& tokeniser)
211 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, name));
213 Model& model = g_models[name];
215 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
216 tokeniser.nextLine();
220 const char* parameter;
221 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, parameter));
222 if(string_equal(parameter, "}"))
224 tokeniser.nextLine();
227 else if(string_equal(parameter, "inherit"))
229 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_parent));
230 tokeniser.nextLine();
232 else if(string_equal(parameter, "remove"))
234 //const char* remove =
235 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
236 tokeniser.nextLine();
238 else if(string_equal(parameter, "mesh"))
240 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_mesh));
241 tokeniser.nextLine();
243 else if(string_equal(parameter, "skin"))
245 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_skin));
246 tokeniser.nextLine();
248 else if(string_equal(parameter, "offset"))
250 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "("));
251 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
252 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
253 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
254 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, ")"));
255 tokeniser.nextLine();
257 else if(string_equal(parameter, "channel"))
259 //const char* channelName =
260 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
261 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "("));
265 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, end));
266 if(string_equal(end, ")"))
268 tokeniser.nextLine();
273 else if(string_equal(parameter, "anim"))
275 CopiedString animName;
276 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animName));
277 const char* animFile;
278 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animFile));
279 model.m_anims.insert(Model::Anims::value_type(animName, animFile));
282 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
284 while(string_equal(token, ","))
286 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animFile));
287 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
290 if(string_equal(token, "{"))
295 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, end));
296 if(string_equal(end, "}"))
298 tokeniser.nextLine();
301 tokeniser.nextLine();
306 tokeniser.ungetToken();
311 globalErrorStream() << "unknown model parameter: " << makeQuoted(parameter) << "\n";
314 tokeniser.nextLine();
319 inline bool char_isSpaceOrTab(char c)
321 return c == ' ' || c == '\t';
324 inline bool char_isNotSpaceOrTab(char c)
326 return !char_isSpaceOrTab(c);
329 template<typename Predicate>
330 inline const char* string_find_if(const char* string, Predicate predicate)
332 for(; *string != 0; ++string)
334 if(predicate(*string))
342 inline const char* string_findFirstSpaceOrTab(const char* string)
344 return string_find_if(string, char_isSpaceOrTab);
347 inline const char* string_findFirstNonSpaceOrTab(const char* string)
349 return string_find_if(string, char_isNotSpaceOrTab);
353 static bool EntityClass_parse(EntityClass& entityClass, Tokeniser& tokeniser)
355 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, entityClass.m_name));
357 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
358 tokeniser.nextLine();
360 StringOutputStream usage(256);
361 StringOutputStream description(256);
362 CopiedString* currentDescription = 0;
363 StringOutputStream* currentString = 0;
368 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, key));
370 const char* last = string_findFirstSpaceOrTab(key);
371 CopiedString first(StringRange(key, last));
373 if(!string_empty(last))
375 last = string_findFirstNonSpaceOrTab(last);
378 if(currentString != 0 && string_equal(key, "\\"))
380 tokeniser.nextLine();
381 *currentString << " ";
382 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, *currentString));
386 if(currentDescription != 0)
388 *currentDescription = description.c_str();
390 currentDescription = 0;
394 if(string_equal(key, "}"))
396 tokeniser.nextLine();
399 else if(string_equal(key, "model"))
402 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
403 entityClass.fixedsize = true;
404 StringOutputStream buffer(256);
405 buffer << PathCleaned(token);
406 entityClass.m_modelpath = buffer.c_str();
408 else if(string_equal(key, "editor_color"))
411 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
412 if(!string_empty(value))
414 entityClass.colorSpecified = true;
415 bool success = string_parse_vector3(value, entityClass.color);
416 ASSERT_MESSAGE(success, "editor_color: parse error");
419 else if(string_equal(key, "editor_ragdoll"))
421 //bool ragdoll = atoi(tokeniser.getToken()) != 0;
422 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
424 else if(string_equal(key, "editor_mins"))
426 entityClass.sizeSpecified = true;
428 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
429 if(!string_empty(value) && !string_equal(value, "?"))
431 entityClass.fixedsize = true;
432 bool success = string_parse_vector3(value, entityClass.mins);
433 ASSERT_MESSAGE(success, "editor_mins: parse error");
436 else if(string_equal(key, "editor_maxs"))
438 entityClass.sizeSpecified = true;
440 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
441 if(!string_empty(value) && !string_equal(value, "?"))
443 entityClass.fixedsize = true;
444 bool success = string_parse_vector3(value, entityClass.maxs);
445 ASSERT_MESSAGE(success, "editor_maxs: parse error");
448 else if(string_equal(key, "editor_usage"))
450 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, usage));
451 currentString = &usage;
453 else if(string_equal_n(key, "editor_usage", 12))
456 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, usage));
457 currentString = &usage;
459 else if(string_equal(key, "editor_rotatable")
460 || string_equal(key, "editor_showangle")
461 || string_equal(key, "editor_mover")
462 || string_equal(key, "editor_model")
463 || string_equal(key, "editor_material")
464 || string_equal(key, "editor_combatnode")
465 || (!string_empty(last) && string_equal(first.c_str(), "editor_gui"))
466 || string_equal_n(key, "editor_copy", 11))
468 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
470 else if(!string_empty(last) && (string_equal(first.c_str(), "editor_var") || string_equal(first.c_str(), "editor_string")))
472 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
473 attribute.m_type = "string";
474 currentDescription = &attribute.m_description;
475 currentString = &description;
476 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
478 else if(!string_empty(last) && string_equal(first.c_str(), "editor_float"))
480 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
481 attribute.m_type = "string";
482 currentDescription = &attribute.m_description;
483 currentString = &description;
484 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
486 else if(!string_empty(last) && string_equal(first.c_str(), "editor_snd"))
488 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
489 attribute.m_type = "sound";
490 currentDescription = &attribute.m_description;
491 currentString = &description;
492 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
494 else if(!string_empty(last) && string_equal(first.c_str(), "editor_bool"))
496 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
497 attribute.m_type = "boolean";
498 currentDescription = &attribute.m_description;
499 currentString = &description;
500 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
502 else if(!string_empty(last) && string_equal(first.c_str(), "editor_int"))
504 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
505 attribute.m_type = "integer";
506 currentDescription = &attribute.m_description;
507 currentString = &description;
508 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
510 else if(!string_empty(last) && string_equal(first.c_str(), "editor_model"))
512 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
513 attribute.m_type = "model";
514 currentDescription = &attribute.m_description;
515 currentString = &description;
516 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
518 else if(!string_empty(last) && string_equal(first.c_str(), "editor_color"))
520 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
521 attribute.m_type = "color";
522 currentDescription = &attribute.m_description;
523 currentString = &description;
524 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
526 else if(!string_empty(last) && (string_equal(first.c_str(), "editor_material") || string_equal(first.c_str(), "editor_mat")))
528 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
529 attribute.m_type = "shader";
530 currentDescription = &attribute.m_description;
531 currentString = &description;
532 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
534 else if(string_equal(key, "inherit"))
536 entityClass.inheritanceResolved = false;
537 ASSERT_MESSAGE(entityClass.m_parent.empty(), "only one 'inherit' supported per entityDef");
539 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
540 entityClass.m_parent.push_back(token);
542 // begin quake4-specific keys
543 else if(string_equal(key, "editor_targetonsel"))
545 //const char* value =
546 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
548 else if(string_equal(key, "editor_menu"))
550 //const char* value =
551 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
553 else if(string_equal(key, "editor_ignore"))
555 //const char* value =
556 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
558 // end quake4-specific keys
561 CopiedString tmp(key);
562 ASSERT_MESSAGE(!string_equal_n(key, "editor_", 7), "unsupported editor key: " << makeQuoted(key));
563 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, key).second;
564 attribute.m_type = "string";
566 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
567 if(string_equal(value, "}")) // hack for quake4 powerups.def bug
569 globalErrorStream() << "entityDef " << makeQuoted(entityClass.m_name.c_str()) << " key " << makeQuoted(tmp.c_str()) << " has no value\n";
574 attribute.m_value = value;
577 tokeniser.nextLine();
580 entityClass.m_comments = usage.c_str();
582 if(string_equal(entityClass.m_name.c_str(), "light"))
585 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "light_radius").second;
586 attribute.m_type = "vector3";
587 attribute.m_value = "300 300 300";
590 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "light_center").second;
591 attribute.m_type = "vector3";
594 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "noshadows").second;
595 attribute.m_type = "boolean";
596 attribute.m_value = "0";
599 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "nospecular").second;
600 attribute.m_type = "boolean";
601 attribute.m_value = "0";
604 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "nodiffuse").second;
605 attribute.m_type = "boolean";
606 attribute.m_value = "0";
609 EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "falloff").second;
610 attribute.m_type = "real";
617 bool EntityClassDoom3_parseEntityDef(Tokeniser& tokeniser)
619 EntityClass* entityClass = Eclass_Alloc();
620 entityClass->free = &Eclass_Free;
622 if(!EntityClass_parse(*entityClass, tokeniser))
624 eclass_capture_state(entityClass); // finish constructing the entity so that it can be destroyed cleanly.
625 entityClass->free(entityClass);
629 EntityClass* inserted = EntityClassDoom3_insertUnique(entityClass);
630 if(inserted != entityClass)
632 globalErrorStream() << "entityDef " << entityClass->name() << " is already defined, second definition ignored\n";
633 eclass_capture_state(entityClass); // finish constructing the entity so that it can be destroyed cleanly.
634 entityClass->free(entityClass);
639 bool EntityClassDoom3_parseBlock(Tokeniser& tokeniser, const char* blockType)
641 if(string_equal(blockType, "entityDef"))
643 return EntityClassDoom3_parseEntityDef(tokeniser);
645 else if(string_equal(blockType, "model"))
647 return EntityClassDoom3_parseModel(tokeniser);
651 return EntityClassDoom3_parseUnknown(tokeniser);
655 bool EntityClassDoom3_parse(TextInputStream& inputStream, const char* filename)
657 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(inputStream);
659 tokeniser.nextLine();
663 const char* blockType = tokeniser.getToken();
668 CopiedString tmp(blockType);
669 if(!EntityClassDoom3_parseBlock(tokeniser, tmp.c_str()))
671 globalErrorStream() << GlobalFileSystem().findFile(filename) << filename << ":" << tokeniser.getLine() << ": " << tmp.c_str() << " parse failed, skipping rest of file\n";
680 void EntityClassDoom3_loadFile(const char* filename)
682 globalOutputStream() << "parsing entity classes from " << makeQuoted(filename) << "\n";
684 StringOutputStream fullname(256);
685 fullname << "def/" << filename;
687 ArchiveTextFile* file = GlobalFileSystem().openTextFile(fullname.c_str());
690 EntityClassDoom3_parse(file->getInputStream(), fullname.c_str());
695 EntityClass* EntityClassDoom3_findOrInsert(const char *name, bool has_brushes)
697 ASSERT_NOTNULL(name);
699 if(string_empty(name))
701 return g_EntityClassDoom3_bad;
704 EntityClasses::iterator i = g_EntityClassDoom3_classes.find(name);
705 if(i != g_EntityClassDoom3_classes.end()
706 //&& string_equal((*i).first, name)
712 EntityClass* e = EntityClass_Create_Default(name, has_brushes);
713 EntityClass* inserted = EntityClassDoom3_insertUnique(e);
714 ASSERT_MESSAGE(inserted == e, "");
718 const ListAttributeType* EntityClassDoom3_findListType(const char* name)
724 void EntityClass_resolveInheritance(EntityClass* derivedClass)
726 if(derivedClass->inheritanceResolved == false)
728 derivedClass->inheritanceResolved = true;
729 EntityClasses::iterator i = g_EntityClassDoom3_classes.find(derivedClass->m_parent.front().c_str());
730 if(i == g_EntityClassDoom3_classes.end())
732 globalErrorStream() << "failed to find entityDef " << makeQuoted(derivedClass->m_parent.front().c_str()) << " inherited by " << makeQuoted(derivedClass->m_name.c_str()) << "\n";
736 EntityClass* parentClass = (*i).second;
737 EntityClass_resolveInheritance(parentClass);
738 if(!derivedClass->colorSpecified)
740 derivedClass->colorSpecified = parentClass->colorSpecified;
741 derivedClass->color = parentClass->color;
743 if(!derivedClass->sizeSpecified)
745 derivedClass->sizeSpecified = parentClass->sizeSpecified;
746 derivedClass->mins = parentClass->mins;
747 derivedClass->maxs = parentClass->maxs;
748 derivedClass->fixedsize = parentClass->fixedsize;
751 for(EntityClassAttributes::iterator j = parentClass->m_attributes.begin(); j != parentClass->m_attributes.end(); ++j)
753 EntityClass_insertAttribute(*derivedClass, (*j).first.c_str(), (*j).second);
759 class EntityClassDoom3 : public ModuleObserver
761 std::size_t m_unrealised;
762 ModuleObservers m_observers;
764 EntityClassDoom3() : m_unrealised(2)
769 if(--m_unrealised == 0)
771 globalOutputStream() << "searching vfs directory " << makeQuoted("def") << " for *.def\n";
772 GlobalFileSystem().forEachFile("def/", "def", FreeCaller1<const char*, EntityClassDoom3_loadFile>());
775 for(Models::iterator i = g_models.begin(); i != g_models.end(); ++i)
777 Model_resolveInheritance((*i).first.c_str(), (*i).second);
781 for(EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i)
783 EntityClass_resolveInheritance((*i).second);
784 if(!string_empty((*i).second->m_modelpath.c_str()))
786 Models::iterator j = g_models.find((*i).second->m_modelpath);
787 if(j != g_models.end())
789 (*i).second->m_modelpath = (*j).second.m_mesh;
790 (*i).second->m_skin = (*j).second.m_skin;
793 eclass_capture_state((*i).second);
795 StringOutputStream usage(256);
797 usage << "-------- KEYS --------\n";
799 for(EntityClassAttributes::iterator j = (*i).second->m_attributes.begin(); j != (*i).second->m_attributes.end(); ++j)
801 const char* name = EntityClassAttributePair_getName(*j);
802 const char* description = EntityClassAttributePair_getDescription(*j);
803 if(!string_equal(name, description))
805 usage << EntityClassAttributePair_getName(*j) << " : " << EntityClassAttributePair_getDescription(*j) << "\n";
809 (*i).second->m_comments = usage.c_str();
813 m_observers.realise();
818 if(++m_unrealised == 1)
820 m_observers.unrealise();
821 EntityClassDoom3_clear();
824 void attach(ModuleObserver& observer)
826 m_observers.attach(observer);
828 void detach(ModuleObserver& observer)
830 m_observers.detach(observer);
834 EntityClassDoom3 g_EntityClassDoom3;
836 void EntityClassDoom3_attach(ModuleObserver& observer)
838 g_EntityClassDoom3.attach(observer);
840 void EntityClassDoom3_detach(ModuleObserver& observer)
842 g_EntityClassDoom3.detach(observer);
845 void EntityClassDoom3_realise()
847 g_EntityClassDoom3.realise();
849 void EntityClassDoom3_unrealise()
851 g_EntityClassDoom3.unrealise();
854 void EntityClassDoom3_construct()
856 GlobalFileSystem().attach(g_EntityClassDoom3);
858 // start by creating the default unknown eclass
859 g_EntityClassDoom3_bad = EClass_Create("UNKNOWN_CLASS", Vector3(0.0f, 0.5f, 0.0f), "");
861 EntityClassDoom3_realise();
864 void EntityClassDoom3_destroy()
866 EntityClassDoom3_unrealise();
868 g_EntityClassDoom3_bad->free(g_EntityClassDoom3_bad);
870 GlobalFileSystem().detach(g_EntityClassDoom3);
873 class EntityClassDoom3Dependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef
877 class EntityClassDoom3API
879 EntityClassManager m_eclassmanager;
881 typedef EntityClassManager Type;
882 STRING_CONSTANT(Name, "doom3");
884 EntityClassDoom3API()
886 EntityClassDoom3_construct();
888 m_eclassmanager.findOrInsert = &EntityClassDoom3_findOrInsert;
889 m_eclassmanager.findListType = &EntityClassDoom3_findListType;
890 m_eclassmanager.forEach = &EntityClassDoom3_forEach;
891 m_eclassmanager.attach = &EntityClassDoom3_attach;
892 m_eclassmanager.detach = &EntityClassDoom3_detach;
893 m_eclassmanager.realise = &EntityClassDoom3_realise;
894 m_eclassmanager.unrealise = &EntityClassDoom3_unrealise;
896 ~EntityClassDoom3API()
898 EntityClassDoom3_destroy();
900 EntityClassManager* getTable()
902 return &m_eclassmanager;
906 #include "modulesystem/singletonmodule.h"
907 #include "modulesystem/moduleregistry.h"
909 typedef SingletonModule<EntityClassDoom3API, EntityClassDoom3Dependencies> EntityClassDoom3Module;
910 typedef Static<EntityClassDoom3Module> StaticEntityClassDoom3Module;
911 StaticRegisterModule staticRegisterEntityClassDoom3(StaticEntityClassDoom3Module::instance());