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"
45 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) {
68 (*i).second->free((*i).second);
70 g_EntityClassDoom3_classes.clear();
73 // entityClass will be inserted only if another of the same name does not already exist.
74 // if entityClass was inserted, the same object is returned, otherwise the already-existing object is returned.
75 EntityClass *EntityClassDoom3_insertUnique(EntityClass *entityClass)
77 return (*g_EntityClassDoom3_classes.insert(
78 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) {
84 visitor.visit((*i).second);
88 inline void printParseError(const char *message)
90 globalErrorStream() << message;
93 #define PARSE_RETURN_FALSE_IF_FAIL(expression) do { if (!( expression)) { printParseError(FILE_LINE "\nparse failed: " #expression "\n"); return false; } } while (0)
95 bool EntityClassDoom3_parseToken(Tokeniser &tokeniser)
97 const char *token = tokeniser.getToken();
98 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
102 bool EntityClassDoom3_parseToken(Tokeniser &tokeniser, const char *string)
104 const char *token = tokeniser.getToken();
105 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
106 return string_equal(token, string);
109 bool EntityClassDoom3_parseString(Tokeniser &tokeniser, const char *&s)
111 const char *token = tokeniser.getToken();
112 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
117 bool EntityClassDoom3_parseString(Tokeniser &tokeniser, CopiedString &s)
119 const char *token = tokeniser.getToken();
120 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
125 bool EntityClassDoom3_parseString(Tokeniser &tokeniser, StringOutputStream &s)
127 const char *token = tokeniser.getToken();
128 PARSE_RETURN_FALSE_IF_FAIL(token != 0);
133 bool EntityClassDoom3_parseUnknown(Tokeniser &tokeniser)
136 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
138 //globalOutputStream() << "parsing unknown block " << makeQuoted(name) << "\n";
140 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
141 tokeniser.nextLine();
143 std::size_t depth = 1;
146 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
147 if (string_equal(token, "}")) {
149 tokeniser.nextLine();
152 } else if (string_equal(token, "{")) {
155 tokeniser.nextLine();
166 CopiedString m_parent;
167 typedef std::map<CopiedString, CopiedString> Anims;
170 Model() : m_resolved(false)
175 typedef std::map<CopiedString, Model> Models;
179 void Model_resolveInheritance(const char *name, Model &model)
181 if (model.m_resolved == false) {
182 model.m_resolved = true;
184 if (!string_empty(model.m_parent.c_str())) {
185 Models::iterator i = g_models.find(model.m_parent);
186 if (i == g_models.end()) {
187 globalErrorStream() << "model " << name << " inherits unknown model " << model.m_parent.c_str() << "\n";
189 Model_resolveInheritance((*i).first.c_str(), (*i).second);
190 model.m_mesh = (*i).second.m_mesh;
191 model.m_skin = (*i).second.m_skin;
197 bool EntityClassDoom3_parseModel(Tokeniser &tokeniser)
200 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, name));
202 Model &model = g_models[name];
204 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
205 tokeniser.nextLine();
208 const char *parameter;
209 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, parameter));
210 if (string_equal(parameter, "}")) {
211 tokeniser.nextLine();
213 } else if (string_equal(parameter, "inherit")) {
214 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_parent));
215 tokeniser.nextLine();
216 } else if (string_equal(parameter, "remove")) {
217 //const char* remove =
218 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
219 tokeniser.nextLine();
220 } else if (string_equal(parameter, "mesh")) {
221 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_mesh));
222 tokeniser.nextLine();
223 } else if (string_equal(parameter, "skin")) {
224 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_skin));
225 tokeniser.nextLine();
226 } else if (string_equal(parameter, "offset")) {
227 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "("));
228 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
229 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
230 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
231 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, ")"));
232 tokeniser.nextLine();
233 } else if (string_equal(parameter, "channel")) {
234 //const char* channelName =
235 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
236 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "("));
239 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, end));
240 if (string_equal(end, ")")) {
241 tokeniser.nextLine();
245 } else if (string_equal(parameter, "anim")) {
246 CopiedString animName;
247 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animName));
248 const char *animFile;
249 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animFile));
250 model.m_anims.insert(Model::Anims::value_type(animName, animFile));
253 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
255 while (string_equal(token, ",")) {
256 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animFile));
257 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
260 if (string_equal(token, "{")) {
263 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, end));
264 if (string_equal(end, "}")) {
265 tokeniser.nextLine();
268 tokeniser.nextLine();
271 tokeniser.ungetToken();
274 globalErrorStream() << "unknown model parameter: " << makeQuoted(parameter) << "\n";
277 tokeniser.nextLine();
282 inline bool char_isSpaceOrTab(char c)
284 return c == ' ' || c == '\t';
287 inline bool char_isNotSpaceOrTab(char c)
289 return !char_isSpaceOrTab(c);
292 template<typename Predicate>
293 inline const char *string_find_if(const char *string, Predicate predicate)
295 for (; *string != 0; ++string) {
296 if (predicate(*string)) {
303 inline const char *string_findFirstSpaceOrTab(const char *string)
305 return string_find_if(string, char_isSpaceOrTab);
308 inline const char *string_findFirstNonSpaceOrTab(const char *string)
310 return string_find_if(string, char_isNotSpaceOrTab);
314 static bool EntityClass_parse(EntityClass &entityClass, Tokeniser &tokeniser)
316 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, entityClass.m_name));
318 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
319 tokeniser.nextLine();
321 StringOutputStream usage(256);
322 StringOutputStream description(256);
323 CopiedString *currentDescription = 0;
324 StringOutputStream *currentString = 0;
328 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, key));
330 const char *last = string_findFirstSpaceOrTab(key);
331 CopiedString first(StringRange(key, last));
333 if (!string_empty(last)) {
334 last = string_findFirstNonSpaceOrTab(last);
337 if (currentString != 0 && string_equal(key, "\\")) {
338 tokeniser.nextLine();
339 *currentString << " ";
340 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, *currentString));
344 if (currentDescription != 0) {
345 *currentDescription = description.c_str();
347 currentDescription = 0;
351 if (string_equal(key, "}")) {
352 tokeniser.nextLine();
354 } else if (string_equal(key, "model")) {
356 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
357 entityClass.fixedsize = true;
358 StringOutputStream buffer(256);
359 buffer << PathCleaned(token);
360 entityClass.m_modelpath = buffer.c_str();
361 } else if (string_equal(key, "editor_color")) {
363 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
364 if (!string_empty(value)) {
365 entityClass.colorSpecified = true;
366 bool success = string_parse_vector3(value, entityClass.color);
367 ASSERT_MESSAGE(success, "editor_color: parse error");
369 } else if (string_equal(key, "editor_ragdoll")) {
370 //bool ragdoll = atoi(tokeniser.getToken()) != 0;
371 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
372 } else if (string_equal(key, "editor_mins")) {
373 entityClass.sizeSpecified = true;
375 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
376 if (!string_empty(value) && !string_equal(value, "?")) {
377 entityClass.fixedsize = true;
378 bool success = string_parse_vector3(value, entityClass.mins);
379 ASSERT_MESSAGE(success, "editor_mins: parse error");
381 } else if (string_equal(key, "editor_maxs")) {
382 entityClass.sizeSpecified = true;
384 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
385 if (!string_empty(value) && !string_equal(value, "?")) {
386 entityClass.fixedsize = true;
387 bool success = string_parse_vector3(value, entityClass.maxs);
388 ASSERT_MESSAGE(success, "editor_maxs: parse error");
390 } else if (string_equal(key, "editor_usage")) {
391 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, usage));
392 currentString = &usage;
393 } else if (string_equal_n(key, "editor_usage", 12)) {
394 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, usage));
395 currentString = &usage;
396 } else if (string_equal(key, "editor_rotatable")
397 || string_equal(key, "editor_showangle")
398 || string_equal(key, "editor_showangles") // typo? in prey movables.def
399 || string_equal(key, "editor_mover")
400 || string_equal(key, "editor_model")
401 || string_equal(key, "editor_material")
402 || string_equal(key, "editor_combatnode")
403 || (!string_empty(last) && string_equal(first.c_str(), "editor_gui"))
404 || string_equal_n(key, "editor_copy", 11)) {
405 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
406 } else if (!string_empty(last) &&
407 (string_equal(first.c_str(), "editor_var") || string_equal(first.c_str(), "editor_string"))) {
408 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
409 attribute.m_type = "string";
410 currentDescription = &attribute.m_description;
411 currentString = &description;
412 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
413 } else if (!string_empty(last) && string_equal(first.c_str(), "editor_float")) {
414 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
415 attribute.m_type = "string";
416 currentDescription = &attribute.m_description;
417 currentString = &description;
418 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
419 } else if (!string_empty(last) && string_equal(first.c_str(), "editor_snd")) {
420 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
421 attribute.m_type = "sound";
422 currentDescription = &attribute.m_description;
423 currentString = &description;
424 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
425 } else if (!string_empty(last) && string_equal(first.c_str(), "editor_bool")) {
426 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
427 attribute.m_type = "boolean";
428 currentDescription = &attribute.m_description;
429 currentString = &description;
430 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
431 } else if (!string_empty(last) && string_equal(first.c_str(), "editor_int")) {
432 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
433 attribute.m_type = "integer";
434 currentDescription = &attribute.m_description;
435 currentString = &description;
436 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
437 } else if (!string_empty(last) && string_equal(first.c_str(), "editor_model")) {
438 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
439 attribute.m_type = "model";
440 currentDescription = &attribute.m_description;
441 currentString = &description;
442 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
443 } else if (!string_empty(last) && string_equal(first.c_str(), "editor_color")) {
444 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
445 attribute.m_type = "color";
446 currentDescription = &attribute.m_description;
447 currentString = &description;
448 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
449 } else if (!string_empty(last) &&
450 (string_equal(first.c_str(), "editor_material") || string_equal(first.c_str(), "editor_mat"))) {
451 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, last).second;
452 attribute.m_type = "shader";
453 currentDescription = &attribute.m_description;
454 currentString = &description;
455 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
456 } else if (string_equal(key, "inherit")) {
457 entityClass.inheritanceResolved = false;
458 ASSERT_MESSAGE(entityClass.m_parent.empty(), "only one 'inherit' supported per entityDef");
460 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
461 entityClass.m_parent.push_back(token);
463 // begin quake4-specific keys
464 else if (string_equal(key, "editor_targetonsel")) {
465 //const char* value =
466 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
467 } else if (string_equal(key, "editor_menu")) {
468 //const char* value =
469 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
470 } else if (string_equal(key, "editor_ignore")) {
471 //const char* value =
472 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
474 // end quake4-specific keys
475 // begin ignore prey (unknown/unused?) entity keys
476 else if (string_equal(key, "editor_light")
477 || string_equal(key, "editor_def def_debrisspawner")
478 || string_equal(key, "editor_def def_drop")
479 || string_equal(key, "editor_def def_guihand")
480 || string_equal(key, "editor_def def_mine")) {
481 //const char* value =
482 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
484 // end ignore prey entity keys
486 CopiedString tmp(key);
487 if (string_equal_n(key, "editor_", 7)) {
488 globalErrorStream() << "unsupported editor key " << makeQuoted(key);
490 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, key).second;
491 attribute.m_type = "string";
493 PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
494 if (string_equal(value, "}")) { // hack for quake4 powerups.def bug
495 globalErrorStream() << "entityDef " << makeQuoted(entityClass.m_name.c_str()) << " key "
496 << makeQuoted(tmp.c_str()) << " has no value\n";
499 attribute.m_value = value;
502 tokeniser.nextLine();
505 entityClass.m_comments = usage.c_str();
507 if (string_equal(entityClass.m_name.c_str(), "light")) {
509 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, "light_radius").second;
510 attribute.m_type = "vector3";
511 attribute.m_value = "300 300 300";
514 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, "light_center").second;
515 attribute.m_type = "vector3";
518 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, "noshadows").second;
519 attribute.m_type = "boolean";
520 attribute.m_value = "0";
523 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, "nospecular").second;
524 attribute.m_type = "boolean";
525 attribute.m_value = "0";
528 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, "nodiffuse").second;
529 attribute.m_type = "boolean";
530 attribute.m_value = "0";
533 EntityClassAttribute &attribute = EntityClass_insertAttribute(entityClass, "falloff").second;
534 attribute.m_type = "real";
541 bool EntityClassDoom3_parseEntityDef(Tokeniser &tokeniser)
543 EntityClass *entityClass = Eclass_Alloc();
544 entityClass->free = &Eclass_Free;
546 if (!EntityClass_parse(*entityClass, tokeniser)) {
547 eclass_capture_state(entityClass); // finish constructing the entity so that it can be destroyed cleanly.
548 entityClass->free(entityClass);
552 EntityClass *inserted = EntityClassDoom3_insertUnique(entityClass);
553 if (inserted != entityClass) {
554 globalErrorStream() << "entityDef " << entityClass->name()
555 << " is already defined, second definition ignored\n";
556 eclass_capture_state(entityClass); // finish constructing the entity so that it can be destroyed cleanly.
557 entityClass->free(entityClass);
562 bool EntityClassDoom3_parseBlock(Tokeniser &tokeniser, const char *blockType)
564 if (string_equal(blockType, "entityDef")) {
565 return EntityClassDoom3_parseEntityDef(tokeniser);
566 } else if (string_equal(blockType, "model")) {
567 return EntityClassDoom3_parseModel(tokeniser);
569 return EntityClassDoom3_parseUnknown(tokeniser);
573 bool EntityClassDoom3_parse(TextInputStream &inputStream, const char *filename)
575 Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(inputStream);
577 tokeniser.nextLine();
580 const char *blockType = tokeniser.getToken();
581 if (blockType == 0) {
584 CopiedString tmp(blockType);
585 if (!EntityClassDoom3_parseBlock(tokeniser, tmp.c_str())) {
586 globalErrorStream() << GlobalFileSystem().findFile(filename) << filename << ":"
587 << (unsigned int) tokeniser.getLine() << ": " << tmp.c_str()
588 << " parse failed, skipping rest of file\n";
597 void EntityClassDoom3_loadFile(const char *filename)
599 globalOutputStream() << "parsing entity classes from " << makeQuoted(filename) << "\n";
601 StringOutputStream fullname(256);
602 fullname << "def/" << filename;
604 ArchiveTextFile *file = GlobalFileSystem().openTextFile(fullname.c_str());
606 EntityClassDoom3_parse(file->getInputStream(), fullname.c_str());
611 EntityClass *EntityClassDoom3_findOrInsert(const char *name, bool has_brushes)
613 ASSERT_NOTNULL(name);
615 if (string_empty(name)) {
616 return g_EntityClassDoom3_bad;
619 EntityClasses::iterator i = g_EntityClassDoom3_classes.find(name);
620 if (i != g_EntityClassDoom3_classes.end()
621 //&& string_equal((*i).first, name)
626 EntityClass *e = EntityClass_Create_Default(name, has_brushes);
627 EntityClass *inserted = EntityClassDoom3_insertUnique(e);
628 ASSERT_MESSAGE(inserted == e, "");
632 const ListAttributeType *EntityClassDoom3_findListType(const char *name)
638 void EntityClass_resolveInheritance(EntityClass *derivedClass)
640 if (derivedClass->inheritanceResolved == false) {
641 derivedClass->inheritanceResolved = true;
642 EntityClasses::iterator i = g_EntityClassDoom3_classes.find(derivedClass->m_parent.front().c_str());
643 if (i == g_EntityClassDoom3_classes.end()) {
644 globalErrorStream() << "failed to find entityDef " << makeQuoted(derivedClass->m_parent.front().c_str())
645 << " inherited by " << makeQuoted(derivedClass->m_name.c_str()) << "\n";
647 EntityClass *parentClass = (*i).second;
648 EntityClass_resolveInheritance(parentClass);
649 if (!derivedClass->colorSpecified) {
650 derivedClass->colorSpecified = parentClass->colorSpecified;
651 derivedClass->color = parentClass->color;
653 if (!derivedClass->sizeSpecified) {
654 derivedClass->sizeSpecified = parentClass->sizeSpecified;
655 derivedClass->mins = parentClass->mins;
656 derivedClass->maxs = parentClass->maxs;
657 derivedClass->fixedsize = parentClass->fixedsize;
660 for (EntityClassAttributes::iterator j = parentClass->m_attributes.begin();
661 j != parentClass->m_attributes.end(); ++j) {
662 EntityClass_insertAttribute(*derivedClass, (*j).first.c_str(), (*j).second);
668 class EntityClassDoom3 : public ModuleObserver {
669 std::size_t m_unrealised;
670 ModuleObservers m_observers;
672 EntityClassDoom3() : m_unrealised(2)
678 if (--m_unrealised == 0) {
679 globalOutputStream() << "searching vfs directory " << makeQuoted("def") << " for *.def\n";
680 GlobalFileSystem().forEachFile("def/", "def", makeCallbackF(EntityClassDoom3_loadFile));
683 for (Models::iterator i = g_models.begin(); i != g_models.end(); ++i) {
684 Model_resolveInheritance((*i).first.c_str(), (*i).second);
688 for (EntityClasses::iterator i = g_EntityClassDoom3_classes.begin();
689 i != g_EntityClassDoom3_classes.end(); ++i) {
690 EntityClass_resolveInheritance((*i).second);
691 if (!string_empty((*i).second->m_modelpath.c_str())) {
692 Models::iterator j = g_models.find((*i).second->m_modelpath);
693 if (j != g_models.end()) {
694 (*i).second->m_modelpath = (*j).second.m_mesh;
695 (*i).second->m_skin = (*j).second.m_skin;
698 eclass_capture_state((*i).second);
700 StringOutputStream usage(256);
702 usage << "-------- NOTES --------\n";
704 if (!string_empty((*i).second->m_comments.c_str())) {
705 usage << (*i).second->m_comments.c_str() << "\n";
708 usage << "\n-------- KEYS --------\n";
710 for (EntityClassAttributes::iterator j = (*i).second->m_attributes.begin();
711 j != (*i).second->m_attributes.end(); ++j) {
712 const char *name = EntityClassAttributePair_getName(*j);
713 const char *description = EntityClassAttributePair_getDescription(*j);
714 if (!string_equal(name, description)) {
715 usage << EntityClassAttributePair_getName(*j) << " : "
716 << EntityClassAttributePair_getDescription(*j) << "\n";
720 (*i).second->m_comments = usage.c_str();
724 m_observers.realise();
730 if (++m_unrealised == 1) {
731 m_observers.unrealise();
732 EntityClassDoom3_clear();
736 void attach(ModuleObserver &observer)
738 m_observers.attach(observer);
741 void detach(ModuleObserver &observer)
743 m_observers.detach(observer);
747 EntityClassDoom3 g_EntityClassDoom3;
749 void EntityClassDoom3_attach(ModuleObserver &observer)
751 g_EntityClassDoom3.attach(observer);
754 void EntityClassDoom3_detach(ModuleObserver &observer)
756 g_EntityClassDoom3.detach(observer);
759 void EntityClassDoom3_realise()
761 g_EntityClassDoom3.realise();
764 void EntityClassDoom3_unrealise()
766 g_EntityClassDoom3.unrealise();
769 void EntityClassDoom3_construct()
771 GlobalFileSystem().attach(g_EntityClassDoom3);
773 // start by creating the default unknown eclass
774 g_EntityClassDoom3_bad = EClass_Create("UNKNOWN_CLASS", Vector3(0.0f, 0.5f, 0.0f), "");
776 EntityClassDoom3_realise();
779 void EntityClassDoom3_destroy()
781 EntityClassDoom3_unrealise();
783 g_EntityClassDoom3_bad->free(g_EntityClassDoom3_bad);
785 GlobalFileSystem().detach(g_EntityClassDoom3);
788 class EntityClassDoom3Dependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef {
791 class EntityClassDoom3API {
792 EntityClassManager m_eclassmanager;
794 typedef EntityClassManager Type;
796 STRING_CONSTANT(Name, "doom3");
798 EntityClassDoom3API()
800 EntityClassDoom3_construct();
802 m_eclassmanager.findOrInsert = &EntityClassDoom3_findOrInsert;
803 m_eclassmanager.findListType = &EntityClassDoom3_findListType;
804 m_eclassmanager.forEach = &EntityClassDoom3_forEach;
805 m_eclassmanager.attach = &EntityClassDoom3_attach;
806 m_eclassmanager.detach = &EntityClassDoom3_detach;
807 m_eclassmanager.realise = &EntityClassDoom3_realise;
808 m_eclassmanager.unrealise = &EntityClassDoom3_unrealise;
811 ~EntityClassDoom3API()
813 EntityClassDoom3_destroy();
816 EntityClassManager *getTable()
818 return &m_eclassmanager;
822 #include "modulesystem/singletonmodule.h"
823 #include "modulesystem/moduleregistry.h"
825 typedef SingletonModule<EntityClassDoom3API, EntityClassDoom3Dependencies> EntityClassDoom3Module;
826 typedef Static<EntityClassDoom3Module> StaticEntityClassDoom3Module;
827 StaticRegisterModule staticRegisterEntityClassDoom3(StaticEntityClassDoom3Module::instance());