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
88 #define PARSE_RETURN_FALSE_IF_FAIL( expression ) do{ if ( !( expression ) ) { printParseError( FILE_LINE "\nparse failed: " # expression "\n" ); return false; } }while( 0 )
90 bool EntityClassDoom3_parseToken( Tokeniser& tokeniser ){
91 const char* token = tokeniser.getToken();
92 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
96 bool EntityClassDoom3_parseToken( Tokeniser& tokeniser, const char* string ){
97 const char* token = tokeniser.getToken();
98 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
99 return string_equal( token, string );
102 bool EntityClassDoom3_parseString( Tokeniser& tokeniser, const char*& s ){
103 const char* token = tokeniser.getToken();
104 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
109 bool EntityClassDoom3_parseString( Tokeniser& tokeniser, CopiedString& s ){
110 const char* token = tokeniser.getToken();
111 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
116 bool EntityClassDoom3_parseString( Tokeniser& tokeniser, StringOutputStream& s ){
117 const char* token = tokeniser.getToken();
118 PARSE_RETURN_FALSE_IF_FAIL( token != 0 );
123 bool EntityClassDoom3_parseUnknown( Tokeniser& tokeniser ){
125 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
127 //globalOutputStream() << "parsing unknown block " << makeQuoted(name) << "\n";
129 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "{" ) );
130 tokeniser.nextLine();
132 std::size_t depth = 1;
136 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
137 if ( string_equal( token, "}" ) ) {
138 if ( --depth == 0 ) {
139 tokeniser.nextLine();
143 else if ( string_equal( token, "{" ) ) {
146 tokeniser.nextLine();
158 CopiedString m_parent;
159 typedef std::map<CopiedString, CopiedString> Anims;
161 Model() : m_resolved( false ){
165 typedef std::map<CopiedString, Model> Models;
169 void Model_resolveInheritance( const char* name, Model& model ){
170 if ( model.m_resolved == false ) {
171 model.m_resolved = true;
173 if ( !string_empty( model.m_parent.c_str() ) ) {
174 Models::iterator i = g_models.find( model.m_parent );
175 if ( i == g_models.end() ) {
176 globalErrorStream() << "model " << name << " inherits unknown model " << model.m_parent.c_str() << "\n";
180 Model_resolveInheritance( ( *i ).first.c_str(), ( *i ).second );
181 model.m_mesh = ( *i ).second.m_mesh;
182 model.m_skin = ( *i ).second.m_skin;
188 bool EntityClassDoom3_parseModel( Tokeniser& tokeniser ){
190 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, name ) );
192 Model& model = g_models[name];
194 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "{" ) );
195 tokeniser.nextLine();
199 const char* parameter;
200 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, parameter ) );
201 if ( string_equal( parameter, "}" ) ) {
202 tokeniser.nextLine();
205 else if ( string_equal( parameter, "inherit" ) ) {
206 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, model.m_parent ) );
207 tokeniser.nextLine();
209 else if ( string_equal( parameter, "remove" ) ) {
210 //const char* remove =
211 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
212 tokeniser.nextLine();
214 else if ( string_equal( parameter, "mesh" ) ) {
215 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, model.m_mesh ) );
216 tokeniser.nextLine();
218 else if ( string_equal( parameter, "skin" ) ) {
219 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, model.m_skin ) );
220 tokeniser.nextLine();
222 else if ( string_equal( parameter, "offset" ) ) {
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 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, ")" ) );
228 tokeniser.nextLine();
230 else if ( string_equal( parameter, "channel" ) ) {
231 //const char* channelName =
232 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
233 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser, "(" ) );
237 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, end ) );
238 if ( string_equal( end, ")" ) ) {
239 tokeniser.nextLine();
244 else if ( string_equal( parameter, "anim" ) ) {
245 CopiedString animName;
246 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, animName ) );
247 const char* animFile;
248 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, animFile ) );
249 model.m_anims.insert( Model::Anims::value_type( animName, animFile ) );
252 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
254 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, "{" ) ) {
264 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, end ) );
265 if ( string_equal( end, "}" ) ) {
266 tokeniser.nextLine();
269 tokeniser.nextLine();
274 tokeniser.ungetToken();
279 globalErrorStream() << "unknown model parameter: " << makeQuoted( parameter ) << "\n";
282 tokeniser.nextLine();
287 inline bool char_isSpaceOrTab( char c ){
288 return c == ' ' || c == '\t';
291 inline bool char_isNotSpaceOrTab( char c ){
292 return !char_isSpaceOrTab( c );
295 template<typename Predicate>
296 inline const char* string_find_if( const char* string, Predicate predicate ){
297 for (; *string != 0; ++string )
299 if ( predicate( *string ) ) {
306 inline const char* string_findFirstSpaceOrTab( const char* string ){
307 return string_find_if( string, char_isSpaceOrTab );
310 inline const char* string_findFirstNonSpaceOrTab( const char* string ){
311 return string_find_if( string, char_isNotSpaceOrTab );
315 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;
329 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, key ) );
331 const char* last = string_findFirstSpaceOrTab( key );
332 CopiedString first( StringRange( key, last ) );
334 if ( !string_empty( last ) ) {
335 last = string_findFirstNonSpaceOrTab( last );
338 if ( currentString != 0 && string_equal( key, "\\" ) ) {
339 tokeniser.nextLine();
340 *currentString << " ";
341 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, *currentString ) );
345 if ( currentDescription != 0 ) {
346 *currentDescription = description.c_str();
348 currentDescription = 0;
352 if ( string_equal( key, "}" ) ) {
353 tokeniser.nextLine();
356 else if ( string_equal( key, "model" ) ) {
358 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
359 entityClass.fixedsize = true;
360 StringOutputStream buffer( 256 );
361 buffer << PathCleaned( token );
362 entityClass.m_modelpath = buffer.c_str();
364 else if ( string_equal( key, "editor_color" ) ) {
366 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
367 if ( !string_empty( value ) ) {
368 entityClass.colorSpecified = true;
369 bool success = string_parse_vector3( value, entityClass.color );
370 ASSERT_MESSAGE( success, "editor_color: parse error" );
373 else if ( string_equal( key, "editor_ragdoll" ) ) {
374 //bool ragdoll = atoi(tokeniser.getToken()) != 0;
375 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
377 else if ( string_equal( key, "editor_mins" ) ) {
378 entityClass.sizeSpecified = true;
380 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
381 if ( !string_empty( value ) && !string_equal( value, "?" ) ) {
382 entityClass.fixedsize = true;
383 bool success = string_parse_vector3( value, entityClass.mins );
384 ASSERT_MESSAGE( success, "editor_mins: parse error" );
387 else if ( string_equal( key, "editor_maxs" ) ) {
388 entityClass.sizeSpecified = true;
390 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
391 if ( !string_empty( value ) && !string_equal( value, "?" ) ) {
392 entityClass.fixedsize = true;
393 bool success = string_parse_vector3( value, entityClass.maxs );
394 ASSERT_MESSAGE( success, "editor_maxs: parse error" );
397 else if ( string_equal( key, "editor_usage" ) ) {
398 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, usage ) );
399 currentString = &usage;
401 else if ( string_equal_n( key, "editor_usage", 12 ) ) {
402 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, usage ) );
403 currentString = &usage;
405 else if ( string_equal( key, "editor_rotatable" )
406 || string_equal( key, "editor_showangle" )
407 || string_equal( key, "editor_showangles" ) // typo? in prey movables.def
408 || string_equal( key, "editor_mover" )
409 || string_equal( key, "editor_model" )
410 || string_equal( key, "editor_material" )
411 || string_equal( key, "editor_combatnode" )
412 || ( !string_empty( last ) && string_equal( first.c_str(), "editor_gui" ) )
413 || string_equal_n( key, "editor_copy", 11 ) ) {
414 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
416 else if ( !string_empty( last ) && ( string_equal( first.c_str(), "editor_var" ) || string_equal( first.c_str(), "editor_string" ) ) ) {
417 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
418 attribute.m_type = "string";
419 currentDescription = &attribute.m_description;
420 currentString = &description;
421 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
423 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_float" ) ) {
424 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
425 attribute.m_type = "string";
426 currentDescription = &attribute.m_description;
427 currentString = &description;
428 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
430 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_snd" ) ) {
431 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
432 attribute.m_type = "sound";
433 currentDescription = &attribute.m_description;
434 currentString = &description;
435 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
437 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_bool" ) ) {
438 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
439 attribute.m_type = "boolean";
440 currentDescription = &attribute.m_description;
441 currentString = &description;
442 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
444 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_int" ) ) {
445 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
446 attribute.m_type = "integer";
447 currentDescription = &attribute.m_description;
448 currentString = &description;
449 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
451 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_model" ) ) {
452 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
453 attribute.m_type = "model";
454 currentDescription = &attribute.m_description;
455 currentString = &description;
456 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
458 else if ( !string_empty( last ) && string_equal( first.c_str(), "editor_color" ) ) {
459 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
460 attribute.m_type = "color";
461 currentDescription = &attribute.m_description;
462 currentString = &description;
463 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
465 else if ( !string_empty( last ) && ( string_equal( first.c_str(), "editor_material" ) || string_equal( first.c_str(), "editor_mat" ) ) ) {
466 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, last ).second;
467 attribute.m_type = "shader";
468 currentDescription = &attribute.m_description;
469 currentString = &description;
470 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, description ) );
472 else if ( string_equal( key, "inherit" ) ) {
473 entityClass.inheritanceResolved = false;
474 ASSERT_MESSAGE( entityClass.m_parent.empty(), "only one 'inherit' supported per entityDef" );
476 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, token ) );
477 entityClass.m_parent.push_back( token );
479 // begin quake4-specific keys
480 else if ( string_equal( key, "editor_targetonsel" ) ) {
481 //const char* value =
482 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
484 else if ( string_equal( key, "editor_menu" ) ) {
485 //const char* value =
486 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
488 else if ( string_equal( key, "editor_ignore" ) ) {
489 //const char* value =
490 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
492 // end quake4-specific keys
493 // begin ignore prey (unknown/unused?) entity keys
494 else if ( string_equal( key, "editor_light" )
495 || string_equal( key, "editor_def def_debrisspawner" )
496 || string_equal( key, "editor_def def_drop" )
497 || string_equal( key, "editor_def def_guihand" )
498 || string_equal( key, "editor_def def_mine" ) ) {
499 //const char* value =
500 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseToken( tokeniser ) );
502 // end ignore prey entity keys
505 CopiedString tmp( key );
506 //ASSERT_MESSAGE( !string_equal_n( key, "editor_", 7 ), "unsupported editor key: " << makeQuoted( key ) );
507 if ( string_equal_n( key, "editor_", 7 ) ) {
508 globalErrorStream() << "unsupported editor key " << makeQuoted( key ) ;
510 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, key ).second;
511 attribute.m_type = "string";
513 PARSE_RETURN_FALSE_IF_FAIL( EntityClassDoom3_parseString( tokeniser, value ) );
514 if ( string_equal( value, "}" ) ) { // hack for quake4 powerups.def bug
515 globalErrorStream() << "entityDef " << makeQuoted( entityClass.m_name.c_str() ) << " key " << makeQuoted( tmp.c_str() ) << " has no value\n";
520 attribute.m_value = value;
523 tokeniser.nextLine();
526 entityClass.m_comments = usage.c_str();
528 if ( string_equal( entityClass.m_name.c_str(), "light" ) ) {
530 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "light_radius" ).second;
531 attribute.m_type = "vector3";
532 attribute.m_value = "300 300 300";
535 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "light_center" ).second;
536 attribute.m_type = "vector3";
539 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "noshadows" ).second;
540 attribute.m_type = "boolean";
541 attribute.m_value = "0";
544 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "nospecular" ).second;
545 attribute.m_type = "boolean";
546 attribute.m_value = "0";
549 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "nodiffuse" ).second;
550 attribute.m_type = "boolean";
551 attribute.m_value = "0";
554 EntityClassAttribute& attribute = EntityClass_insertAttribute( entityClass, "falloff" ).second;
555 attribute.m_type = "real";
562 bool EntityClassDoom3_parseEntityDef( Tokeniser& tokeniser ){
563 EntityClass* entityClass = Eclass_Alloc();
564 entityClass->free = &Eclass_Free;
566 if ( !EntityClass_parse( *entityClass, tokeniser ) ) {
567 eclass_capture_state( entityClass ); // finish constructing the entity so that it can be destroyed cleanly.
568 entityClass->free( entityClass );
572 EntityClass* inserted = EntityClassDoom3_insertUnique( entityClass );
573 if ( inserted != entityClass ) {
574 globalErrorStream() << "entityDef " << entityClass->name() << " is already defined, second definition ignored\n";
575 eclass_capture_state( entityClass ); // finish constructing the entity so that it can be destroyed cleanly.
576 entityClass->free( entityClass );
581 bool EntityClassDoom3_parseBlock( Tokeniser& tokeniser, const char* blockType ){
582 if ( string_equal( blockType, "entityDef" ) ) {
583 return EntityClassDoom3_parseEntityDef( tokeniser );
585 else if ( string_equal( blockType, "model" ) ) {
586 return EntityClassDoom3_parseModel( tokeniser );
590 return EntityClassDoom3_parseUnknown( tokeniser );
594 bool EntityClassDoom3_parse( TextInputStream& inputStream, const char* filename ){
595 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( inputStream );
597 tokeniser.nextLine();
601 const char* blockType = tokeniser.getToken();
602 if ( blockType == 0 ) {
605 CopiedString tmp( blockType );
606 if ( !EntityClassDoom3_parseBlock( tokeniser, tmp.c_str() ) ) {
607 globalErrorStream() << GlobalFileSystem().findFile( filename ) << filename << ":" << (unsigned int)tokeniser.getLine() << ": " << tmp.c_str() << " parse failed, skipping rest of file\n";
616 void EntityClassDoom3_loadFile( const char* filename ){
617 globalOutputStream() << "parsing entity classes from " << makeQuoted( filename ) << "\n";
619 StringOutputStream fullname( 256 );
620 fullname << "def/" << filename;
622 ArchiveTextFile* file = GlobalFileSystem().openTextFile( fullname.c_str() );
624 EntityClassDoom3_parse( file->getInputStream(), fullname.c_str() );
629 EntityClass* EntityClassDoom3_findOrInsert( const char *name, bool has_brushes ){
630 ASSERT_NOTNULL( name );
632 if ( string_empty( name ) ) {
633 return g_EntityClassDoom3_bad;
636 EntityClasses::iterator i = g_EntityClassDoom3_classes.find( name );
637 if ( i != g_EntityClassDoom3_classes.end()
638 //&& string_equal((*i).first, name)
640 return ( *i ).second;
643 EntityClass* e = EntityClass_Create_Default( name, has_brushes );
644 EntityClass* inserted = EntityClassDoom3_insertUnique( e );
645 ASSERT_MESSAGE( inserted == e, "" );
649 const ListAttributeType* EntityClassDoom3_findListType( const char* name ){
654 void EntityClass_resolveInheritance( EntityClass* derivedClass ){
655 if ( derivedClass->inheritanceResolved == false ) {
656 derivedClass->inheritanceResolved = true;
657 EntityClasses::iterator i = g_EntityClassDoom3_classes.find( derivedClass->m_parent.front().c_str() );
658 if ( i == g_EntityClassDoom3_classes.end() ) {
659 globalErrorStream() << "failed to find entityDef " << makeQuoted( derivedClass->m_parent.front().c_str() ) << " inherited by " << makeQuoted( derivedClass->m_name.c_str() ) << "\n";
663 EntityClass* parentClass = ( *i ).second;
664 EntityClass_resolveInheritance( parentClass );
665 if ( !derivedClass->colorSpecified ) {
666 derivedClass->colorSpecified = parentClass->colorSpecified;
667 derivedClass->color = parentClass->color;
669 if ( !derivedClass->sizeSpecified ) {
670 derivedClass->sizeSpecified = parentClass->sizeSpecified;
671 derivedClass->mins = parentClass->mins;
672 derivedClass->maxs = parentClass->maxs;
673 derivedClass->fixedsize = parentClass->fixedsize;
676 for ( EntityClassAttributes::iterator j = parentClass->m_attributes.begin(); j != parentClass->m_attributes.end(); ++j )
678 EntityClass_insertAttribute( *derivedClass, ( *j ).first.c_str(), ( *j ).second );
684 class EntityClassDoom3 : public ModuleObserver
686 std::size_t m_unrealised;
687 ModuleObservers m_observers;
689 EntityClassDoom3() : m_unrealised( 2 ){
692 if ( --m_unrealised == 0 ) {
693 globalOutputStream() << "searching vfs directory " << makeQuoted( "def" ) << " for *.def\n";
694 GlobalFileSystem().forEachFile( "def/", "def", FreeCaller1<const char*, EntityClassDoom3_loadFile>() );
697 for ( Models::iterator i = g_models.begin(); i != g_models.end(); ++i )
699 Model_resolveInheritance( ( *i ).first.c_str(), ( *i ).second );
703 for ( EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i )
705 EntityClass_resolveInheritance( ( *i ).second );
706 if ( !string_empty( ( *i ).second->m_modelpath.c_str() ) ) {
707 Models::iterator j = g_models.find( ( *i ).second->m_modelpath );
708 if ( j != g_models.end() ) {
709 ( *i ).second->m_modelpath = ( *j ).second.m_mesh;
710 ( *i ).second->m_skin = ( *j ).second.m_skin;
713 eclass_capture_state( ( *i ).second );
715 StringOutputStream usage( 256 );
717 usage << "-------- NOTES --------\n";
719 if ( !string_empty( ( *i ).second->m_comments.c_str() ) ) {
720 usage << ( *i ).second->m_comments.c_str() << "\n";
723 usage << "\n-------- KEYS --------\n";
725 for ( EntityClassAttributes::iterator j = ( *i ).second->m_attributes.begin(); j != ( *i ).second->m_attributes.end(); ++j )
727 const char* name = EntityClassAttributePair_getName( *j );
728 const char* description = EntityClassAttributePair_getDescription( *j );
729 if ( !string_equal( name, description ) ) {
730 usage << EntityClassAttributePair_getName( *j ) << " : " << EntityClassAttributePair_getDescription( *j ) << "\n";
734 ( *i ).second->m_comments = usage.c_str();
738 m_observers.realise();
742 if ( ++m_unrealised == 1 ) {
743 m_observers.unrealise();
744 EntityClassDoom3_clear();
747 void attach( ModuleObserver& observer ){
748 m_observers.attach( observer );
750 void detach( ModuleObserver& observer ){
751 m_observers.detach( observer );
755 EntityClassDoom3 g_EntityClassDoom3;
757 void EntityClassDoom3_attach( ModuleObserver& observer ){
758 g_EntityClassDoom3.attach( observer );
760 void EntityClassDoom3_detach( ModuleObserver& observer ){
761 g_EntityClassDoom3.detach( observer );
764 void EntityClassDoom3_realise(){
765 g_EntityClassDoom3.realise();
767 void EntityClassDoom3_unrealise(){
768 g_EntityClassDoom3.unrealise();
771 void EntityClassDoom3_construct(){
772 GlobalFileSystem().attach( g_EntityClassDoom3 );
774 // start by creating the default unknown eclass
775 g_EntityClassDoom3_bad = EClass_Create( "UNKNOWN_CLASS", Vector3( 0.0f, 0.5f, 0.0f ), "" );
777 EntityClassDoom3_realise();
780 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
792 class EntityClassDoom3API
794 EntityClassManager m_eclassmanager;
796 typedef EntityClassManager Type;
797 STRING_CONSTANT( Name, "doom3" );
799 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;
810 ~EntityClassDoom3API(){
811 EntityClassDoom3_destroy();
813 EntityClassManager* getTable(){
814 return &m_eclassmanager;
818 #include "modulesystem/singletonmodule.h"
819 #include "modulesystem/moduleregistry.h"
821 typedef SingletonModule<EntityClassDoom3API, EntityClassDoom3Dependencies> EntityClassDoom3Module;
822 typedef Static<EntityClassDoom3Module> StaticEntityClassDoom3Module;
823 StaticRegisterModule staticRegisterEntityClassDoom3( StaticEntityClassDoom3Module::instance() );