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 ){
48 const char* c_str() const {
53 inline bool operator<( const RawString& self, const RawString& other ){
54 return string_less_nocase( self.c_str(), other.c_str() );
57 typedef std::map<RawString, EntityClass*> EntityClasses;
58 EntityClasses g_EntityClassDoom3_classes;
59 EntityClass *g_EntityClassDoom3_bad = 0;
62 void EntityClassDoom3_clear(){
63 for ( EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i )
65 ( *i ).second->free( ( *i ).second );
67 g_EntityClassDoom3_classes.clear();
70 // entityClass will be inserted only if another of the same name does not already exist.
71 // if entityClass was inserted, the same object is returned, otherwise the already-existing object is returned.
72 EntityClass* EntityClassDoom3_insertUnique( EntityClass* entityClass ){
73 return ( *g_EntityClassDoom3_classes.insert( EntityClasses::value_type( entityClass->name(), entityClass ) ).first ).second;
76 void EntityClassDoom3_forEach( EntityClassVisitor& visitor ){
77 for ( EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i )
79 visitor.visit( ( *i ).second );
83 inline void printParseError( const char* message ){
84 globalErrorStream() << message;
87 #define PARSE_RETURN_FALSE_IF_FAIL( expression ) if ( !( expression ) ) { printParseError( FILE_LINE "\nparse failed: " # expression "\n" ); return false; } else
89 bool EntityClassDoom3_parseToken( Tokeniser& tokeniser ){
90 const char* token = tokeniser.getToken();
91 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
95 bool EntityClassDoom3_parseToken( Tokeniser& tokeniser, const char* string ){
96 const char* token = tokeniser.getToken();
97 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
98 return string_equal( token, string );
101 bool EntityClassDoom3_parseString( Tokeniser& tokeniser, const char*& s ){
102 const char* token = tokeniser.getToken();
103 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
108 bool EntityClassDoom3_parseString( Tokeniser& tokeniser, CopiedString& s ){
109 const char* token = tokeniser.getToken();
110 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
115 bool EntityClassDoom3_parseString( Tokeniser& tokeniser, StringOutputStream& s ){
116 const char* token = tokeniser.getToken();
117 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
122 bool EntityClassDoom3_parseUnknown( Tokeniser& tokeniser ){
124 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
126 //globalOutputStream() << "parsing unknown block " << makeQuoted(name) << "\n";
128 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "{" ) );
129 tokeniser.nextLine();
131 std::size_t depth = 1;
135 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
136 if ( string_equal( token, "}" ) ) {
137 if ( --depth == 0 ) {
138 tokeniser.nextLine();
142 else if ( string_equal( token, "{" ) ) {
145 tokeniser.nextLine();
157 CopiedString m_parent;
158 typedef std::map<CopiedString, CopiedString> Anims;
160 Model() : m_resolved( false ){
164 typedef std::map<CopiedString, Model> Models;
168 void Model_resolveInheritance( const char* name, Model& model ){
169 if ( model.m_resolved == false ) {
170 model.m_resolved = true;
172 if ( !string_empty( model.m_parent.c_str() ) ) {
173 Models::iterator i = g_models.find( model.m_parent );
174 if ( i == g_models.end() ) {
175 globalErrorStream() << "model " << name << " inherits unknown model " << model.m_parent.c_str() << "\n";
179 Model_resolveInheritance( ( *i ).first.c_str(), ( *i ).second );
180 model.m_mesh = ( *i ).second.m_mesh;
181 model.m_skin = ( *i ).second.m_skin;
187 bool EntityClassDoom3_parseModel( Tokeniser& tokeniser ){
189 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, name ) );
191 Model& model = g_models[name];
193 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "{" ) );
194 tokeniser.nextLine();
198 const char* parameter;
199 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, parameter ) );
200 if ( string_equal( parameter, "}" ) ) {
201 tokeniser.nextLine();
204 else if ( string_equal( parameter, "inherit" ) ) {
205 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, model.m_parent ) );
206 tokeniser.nextLine();
208 else if ( string_equal( parameter, "remove" ) ) {
209 //const char* remove =
210 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
211 tokeniser.nextLine();
213 else if ( string_equal( parameter, "mesh" ) ) {
214 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, model.m_mesh ) );
215 tokeniser.nextLine();
217 else if ( string_equal( parameter, "skin" ) ) {
218 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, model.m_skin ) );
219 tokeniser.nextLine();
221 else if ( string_equal( parameter, "offset" ) ) {
222 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "(" ) );
223 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
224 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
225 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
226 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, ")" ) );
227 tokeniser.nextLine();
229 else if ( string_equal( parameter, "channel" ) ) {
230 //const char* channelName =
231 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
232 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "(" ) );
236 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, end ) );
237 if ( string_equal( end, ")" ) ) {
238 tokeniser.nextLine();
243 else if ( string_equal( parameter, "anim" ) ) {
244 CopiedString animName;
245 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, animName ) );
246 const char* animFile;
247 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, animFile ) );
248 model.m_anims.insert( Model::Anims::value_type( animName, animFile ) );
251 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
253 while ( string_equal( token, "," ) )
255 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, animFile ) );
256 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
259 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();
273 tokeniser.ungetToken();
278 globalErrorStream() << "unknown model parameter: " << makeQuoted( parameter ) << "\n";
281 tokeniser.nextLine();
286 inline bool char_isSpaceOrTab( char c ){
287 return c == ' ' || c == '\t';
290 inline bool char_isNotSpaceOrTab( char c ){
291 return !char_isSpaceOrTab( c );
294 template<typename Predicate>
295 inline const char* string_find_if( const char* string, Predicate predicate ){
296 for (; *string != 0; ++string )
298 if ( predicate( *string ) ) {
305 inline const char* string_findFirstSpaceOrTab( const char* string ){
306 return string_find_if( string, char_isSpaceOrTab );
309 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 ){
315 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, entityClass.m_name ) );
317 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "{" ) );
318 tokeniser.nextLine();
320 StringOutputStream usage( 256 );
321 StringOutputStream description( 256 );
322 CopiedString* currentDescription = 0;
323 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();
355 else if ( string_equal( key, "model" ) ) {
357 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
358 entityClass.fixedsize = true;
359 StringOutputStream buffer( 256 );
360 buffer << PathCleaned( token );
361 entityClass.m_modelpath = buffer.c_str();
363 else if ( string_equal( key, "editor_color" ) ) {
365 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
366 if ( !string_empty( value ) ) {
367 entityClass.colorSpecified = true;
368 bool success = string_parse_vector3( value, entityClass.color );
369 ASSERT_MESSAGE( success, "editor_color: parse error" );
372 else if ( string_equal( key, "editor_ragdoll" ) ) {
373 //bool ragdoll = atoi(tokeniser.getToken()) != 0;
374 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
376 else if ( string_equal( key, "editor_mins" ) ) {
377 entityClass.sizeSpecified = true;
379 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
380 if ( !string_empty( value ) && !string_equal( value, "?" ) ) {
381 entityClass.fixedsize = true;
382 bool success = string_parse_vector3( value, entityClass.mins );
383 ASSERT_MESSAGE( success, "editor_mins: parse error" );
386 else if ( string_equal( key, "editor_maxs" ) ) {
387 entityClass.sizeSpecified = true;
389 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
390 if ( !string_empty( value ) && !string_equal( value, "?" ) ) {
391 entityClass.fixedsize = true;
392 bool success = string_parse_vector3( value, entityClass.maxs );
393 ASSERT_MESSAGE( success, "editor_maxs: parse error" );
396 else if ( string_equal( key, "editor_usage" ) ) {
397 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, usage ) );
398 currentString = &usage;
400 else if ( string_equal_n( key, "editor_usage", 12 ) ) {
401 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, usage ) );
402 currentString = &usage;
404 else if ( string_equal( key, "editor_rotatable" )
405 || string_equal( key, "editor_showangle" )
406 || string_equal( key, "editor_showangles" ) // typo? in prey movables.def
407 || string_equal( key, "editor_mover" )
408 || string_equal( key, "editor_model" )
409 || string_equal( key, "editor_material" )
410 || string_equal( key, "editor_combatnode" )
411 || ( !string_empty( last ) && string_equal( first.c_str(), "editor_gui" ) )
412 || string_equal_n( key, "editor_copy", 11 ) ) {
413 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
415 else if ( !string_empty( last ) && ( string_equal( first.c_str(), "editor_var" ) || string_equal( first.c_str(), "editor_string" ) ) ) {
416 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
417 attribute.m_type = "string";
418 currentDescription = &attribute.m_description;
419 currentString = &description;
420 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
422 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_float" ) ) {
423 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
424 attribute.m_type = "string";
425 currentDescription = &attribute.m_description;
426 currentString = &description;
427 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
429 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_snd" ) ) {
430 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
431 attribute.m_type = "sound";
432 currentDescription = &attribute.m_description;
433 currentString = &description;
434 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
436 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_bool" ) ) {
437 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
438 attribute.m_type = "boolean";
439 currentDescription = &attribute.m_description;
440 currentString = &description;
441 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
443 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_int" ) ) {
444 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
445 attribute.m_type = "integer";
446 currentDescription = &attribute.m_description;
447 currentString = &description;
448 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
450 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_model" ) ) {
451 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
452 attribute.m_type = "model";
453 currentDescription = &attribute.m_description;
454 currentString = &description;
455 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
457 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_color" ) ) {
458 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
459 attribute.m_type = "color";
460 currentDescription = &attribute.m_description;
461 currentString = &description;
462 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
464 else if ( !string_empty( last ) && ( string_equal( first.c_str(), "editor_material" ) || string_equal( first.c_str(), "editor_mat" ) ) ) {
465 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
466 attribute.m_type = "shader";
467 currentDescription = &attribute.m_description;
468 currentString = &description;
469 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
471 else if ( string_equal( key, "inherit" ) ) {
472 entityClass.inheritanceResolved = false;
473 ASSERT_MESSAGE( entityClass.m_parent.empty(), "only one 'inherit' supported per entityDef" );
475 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
476 entityClass.m_parent.push_back( token );
478 // begin quake4-specific keys
479 else if ( string_equal( key, "editor_targetonsel" ) ) {
480 //const char* value =
481 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
483 else if ( string_equal( key, "editor_menu" ) ) {
484 //const char* value =
485 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
487 else if ( string_equal( key, "editor_ignore" ) ) {
488 //const char* value =
489 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
491 // end quake4-specific keys
492 // begin ignore prey (unknown/unused?) entity keys
493 else if ( string_equal( key, "editor_light" )
494 || string_equal( key, "editor_def def_debrisspawner" )
495 || string_equal( key, "editor_def def_drop" )
496 || string_equal( key, "editor_def def_guihand" )
497 || string_equal( key, "editor_def def_mine" ) ) {
498 //const char* value =
499 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
501 // end ignore prey entity keys
504 CopiedString tmp( key );
505 ASSERT_MESSAGE( !string_equal_n( key, "editor_", 7 ), "unsupported editor key: " << makeQuoted( key ) );
506 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, key ).second;
507 attribute.m_type = "string";
509 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
510 if ( string_equal( value, "}" ) ) { // hack for quake4 powerups.def bug
511 globalErrorStream() << "entityDef " << makeQuoted( entityClass.m_name.c_str() ) << " key " << makeQuoted( tmp.c_str() ) << " has no value\n";
516 attribute.m_value = value;
519 tokeniser.nextLine();
522 entityClass.m_comments = usage.c_str();
524 if ( string_equal( entityClass.m_name.c_str(), "light" ) ) {
526 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "light_radius" ).second;
527 attribute.m_type = "vector3";
528 attribute.m_value = "300 300 300";
531 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "light_center" ).second;
532 attribute.m_type = "vector3";
535 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "noshadows" ).second;
536 attribute.m_type = "boolean";
537 attribute.m_value = "0";
540 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "nospecular" ).second;
541 attribute.m_type = "boolean";
542 attribute.m_value = "0";
545 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "nodiffuse" ).second;
546 attribute.m_type = "boolean";
547 attribute.m_value = "0";
550 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "falloff" ).second;
551 attribute.m_type = "real";
558 bool EntityClassDoom3_parseEntityDef( Tokeniser& tokeniser ){
559 EntityClass* entityClass = Eclass_Alloc();
560 entityClass->free = &Eclass_Free;
562 if ( !EntityClass_parse( *entityClass, tokeniser ) ) {
563 eclass_capture_state( entityClass ); // finish constructing the entity so that it can be destroyed cleanly.
564 entityClass->free( entityClass );
568 EntityClass* inserted = EntityClassDoom3_insertUnique( entityClass );
569 if ( inserted != entityClass ) {
570 globalErrorStream() << "entityDef " << entityClass->name() << " is already defined, second definition ignored\n";
571 eclass_capture_state( entityClass ); // finish constructing the entity so that it can be destroyed cleanly.
572 entityClass->free( entityClass );
577 bool EntityClassDoom3_parseBlock( Tokeniser& tokeniser, const char* blockType ){
578 if ( string_equal( blockType, "entityDef" ) ) {
579 return EntityClassDoom3_parseEntityDef( tokeniser );
581 else if ( string_equal( blockType, "model" ) ) {
582 return EntityClassDoom3_parseModel( tokeniser );
586 return EntityClassDoom3_parseUnknown( tokeniser );
590 bool EntityClassDoom3_parse( TextInputStream& inputStream, const char* filename ){
591 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( inputStream );
593 tokeniser.nextLine();
597 const char* blockType = tokeniser.getToken();
598 if ( blockType == 0 ) {
601 CopiedString tmp( blockType );
602 if ( !EntityClassDoom3_parseBlock( tokeniser, tmp.c_str() ) ) {
603 globalErrorStream() << GlobalFileSystem().findFile( filename ) << filename << ":" << (unsigned int)tokeniser.getLine() << ": " << tmp.c_str() << " parse failed, skipping rest of file\n";
612 void EntityClassDoom3_loadFile( const char* filename ){
613 globalOutputStream() << "parsing entity classes from " << makeQuoted( filename ) << "\n";
615 StringOutputStream fullname( 256 );
616 fullname << "def/" << filename;
618 ArchiveTextFile* file = GlobalFileSystem().openTextFile( fullname.c_str() );
620 EntityClassDoom3_parse( file->getInputStream(), fullname.c_str() );
625 EntityClass* EntityClassDoom3_findOrInsert( const char *name, bool has_brushes ){
626 ASSERT_NOTNULL( name );
628 if ( string_empty( name ) ) {
629 return g_EntityClassDoom3_bad;
632 EntityClasses::iterator i = g_EntityClassDoom3_classes.find( name );
633 if ( i != g_EntityClassDoom3_classes.end()
634 //&& string_equal((*i).first, name)
636 return ( *i ).second;
639 EntityClass* e = EntityClass_Create_Default( name, has_brushes );
640 EntityClass* inserted = EntityClassDoom3_insertUnique( e );
641 ASSERT_MESSAGE( inserted == e, "" );
645 const ListAttributeType* EntityClassDoom3_findListType( const char* name ){
650 void EntityClass_resolveInheritance( EntityClass* derivedClass ){
651 if ( derivedClass->inheritanceResolved == false ) {
652 derivedClass->inheritanceResolved = true;
653 EntityClasses::iterator i = g_EntityClassDoom3_classes.find( derivedClass->m_parent.front().c_str() );
654 if ( i == g_EntityClassDoom3_classes.end() ) {
655 globalErrorStream() << "failed to find entityDef " << makeQuoted( derivedClass->m_parent.front().c_str() ) << " inherited by " << makeQuoted( derivedClass->m_name.c_str() ) << "\n";
659 EntityClass* parentClass = ( *i ).second;
660 EntityClass_resolveInheritance( parentClass );
661 if ( !derivedClass->colorSpecified ) {
662 derivedClass->colorSpecified = parentClass->colorSpecified;
663 derivedClass->color = parentClass->color;
665 if ( !derivedClass->sizeSpecified ) {
666 derivedClass->sizeSpecified = parentClass->sizeSpecified;
667 derivedClass->mins = parentClass->mins;
668 derivedClass->maxs = parentClass->maxs;
669 derivedClass->fixedsize = parentClass->fixedsize;
672 for ( EntityClassAttributes::iterator j = parentClass->m_attributes.begin(); j != parentClass->m_attributes.end(); ++j )
674 EntityClass_insertAttribute( *derivedClass, ( *j ).first.c_str(), ( *j ).second );
680 class EntityClassDoom3 : public ModuleObserver
682 std::size_t m_unrealised;
683 ModuleObservers m_observers;
685 EntityClassDoom3() : m_unrealised( 2 ){
688 if ( --m_unrealised == 0 ) {
689 globalOutputStream() << "searching vfs directory " << makeQuoted( "def" ) << " for *.def\n";
690 GlobalFileSystem().forEachFile( "def/", "def", FreeCaller1<const char*, EntityClassDoom3_loadFile>() );
693 for ( Models::iterator i = g_models.begin(); i != g_models.end(); ++i )
695 Model_resolveInheritance( ( *i ).first.c_str(), ( *i ).second );
699 for ( EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i )
701 EntityClass_resolveInheritance( ( *i ).second );
702 if ( !string_empty( ( *i ).second->m_modelpath.c_str() ) ) {
703 Models::iterator j = g_models.find( ( *i ).second->m_modelpath );
704 if ( j != g_models.end() ) {
705 ( *i ).second->m_modelpath = ( *j ).second.m_mesh;
706 ( *i ).second->m_skin = ( *j ).second.m_skin;
709 eclass_capture_state( ( *i ).second );
711 StringOutputStream usage( 256 );
713 usage << "-------- NOTES --------\n";
715 if ( !string_empty( ( *i ).second->m_comments.c_str() ) ) {
716 usage << ( *i ).second->m_comments.c_str() << "\n";
719 usage << "\n-------- KEYS --------\n";
721 for ( EntityClassAttributes::iterator j = ( *i ).second->m_attributes.begin(); j != ( *i ).second->m_attributes.end(); ++j )
723 const char* name = EntityClassAttributePair_getName( *j );
724 const char* description = EntityClassAttributePair_getDescription( *j );
725 if ( !string_equal( name, description ) ) {
726 usage << EntityClassAttributePair_getName( *j ) << " : " << EntityClassAttributePair_getDescription( *j ) << "\n";
730 ( *i ).second->m_comments = usage.c_str();
734 m_observers.realise();
738 if ( ++m_unrealised == 1 ) {
739 m_observers.unrealise();
740 EntityClassDoom3_clear();
743 void attach( ModuleObserver& observer ){
744 m_observers.attach( observer );
746 void detach( ModuleObserver& observer ){
747 m_observers.detach( observer );
751 EntityClassDoom3 g_EntityClassDoom3;
753 void EntityClassDoom3_attach( ModuleObserver& observer ){
754 g_EntityClassDoom3.attach( observer );
756 void EntityClassDoom3_detach( ModuleObserver& observer ){
757 g_EntityClassDoom3.detach( observer );
760 void EntityClassDoom3_realise(){
761 g_EntityClassDoom3.realise();
763 void EntityClassDoom3_unrealise(){
764 g_EntityClassDoom3.unrealise();
767 void EntityClassDoom3_construct(){
768 GlobalFileSystem().attach( g_EntityClassDoom3 );
770 // start by creating the default unknown eclass
771 g_EntityClassDoom3_bad = EClass_Create( "UNKNOWN_CLASS", Vector3( 0.0f, 0.5f, 0.0f ), "" );
773 EntityClassDoom3_realise();
776 void EntityClassDoom3_destroy(){
777 EntityClassDoom3_unrealise();
779 g_EntityClassDoom3_bad->free( g_EntityClassDoom3_bad );
781 GlobalFileSystem().detach( g_EntityClassDoom3 );
784 class EntityClassDoom3Dependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef
788 class EntityClassDoom3API
790 EntityClassManager m_eclassmanager;
792 typedef EntityClassManager Type;
793 STRING_CONSTANT( Name, "doom3" );
795 EntityClassDoom3API(){
796 EntityClassDoom3_construct();
798 m_eclassmanager.findOrInsert = &EntityClassDoom3_findOrInsert;
799 m_eclassmanager.findListType = &EntityClassDoom3_findListType;
800 m_eclassmanager.forEach = &EntityClassDoom3_forEach;
801 m_eclassmanager.attach = &EntityClassDoom3_attach;
802 m_eclassmanager.detach = &EntityClassDoom3_detach;
803 m_eclassmanager.realise = &EntityClassDoom3_realise;
804 m_eclassmanager.unrealise = &EntityClassDoom3_unrealise;
806 ~EntityClassDoom3API(){
807 EntityClassDoom3_destroy();
809 EntityClassManager* getTable(){
810 return &m_eclassmanager;
814 #include "modulesystem/singletonmodule.h"
815 #include "modulesystem/moduleregistry.h"
817 typedef SingletonModule<EntityClassDoom3API, EntityClassDoom3Dependencies> EntityClassDoom3Module;
818 typedef Static<EntityClassDoom3Module> StaticEntityClassDoom3Module;
819 StaticRegisterModule staticRegisterEntityClassDoom3( StaticEntityClassDoom3Module::instance() );