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
25 #include "selectable.h"
26 #include "namespace.h"
29 #include "entitylib.h"
30 #include "eclasslib.h"
33 #include "targetable.h"
34 #include "uniquenames.h"
36 #include "stream/stringstream.h"
40 #include "miscmodel.h"
43 #include "eclassmodel.h"
45 #include "doom3group.h"
51 inline scene::Node& entity_for_eclass( EntityClass* eclass ){
52 if ( ( string_compare_nocase_n( eclass->name(), "misc_", 5 ) == 0 && string_equal_nocase( eclass->name() + string_length( eclass->name() ) - 5, "model" ) ) // misc_*model (also misc_model) // TODO make classname_* wrapper functions for this
53 || classname_equal( eclass->name(), "model_static" ) ) {
54 return New_MiscModel( eclass );
56 else if ( classname_equal( eclass->name(), "light" )
57 || classname_equal( eclass->name(), "lightJunior" ) ) {
58 return New_Light( eclass );
60 if ( !eclass->fixedsize ) {
61 if ( g_gameType == eGameTypeDoom3 ) {
62 return New_Doom3Group( eclass );
66 return New_Group( eclass );
69 else if ( !string_empty( eclass->modelpath() ) ) {
70 return New_EclassModel( eclass );
74 return New_GenericEntity( eclass );
78 void Entity_setName( Entity& entity, const char* name ){
79 entity.setKeyValue( "name", name );
81 typedef ReferenceCaller1<Entity, const char*, Entity_setName> EntitySetNameCaller;
83 inline Namespaced* Node_getNamespaced( scene::Node& node ){
84 return NodeTypeCast<Namespaced>::cast( node );
87 inline scene::Node& node_for_eclass( EntityClass* eclass ){
88 scene::Node& node = entity_for_eclass( eclass );
89 Node_getEntity( node )->setKeyValue( "classname", eclass->name() );
91 if ( g_gameType == eGameTypeDoom3
92 && string_not_empty( eclass->name() )
93 && !string_equal( eclass->name(), "worldspawn" )
94 && !string_equal( eclass->name(), "UNKNOWN_CLASS" ) ) {
96 strcpy( buffer, eclass->name() );
97 strcat( buffer, "_1" );
98 GlobalNamespace().makeUnique( buffer, EntitySetNameCaller( *Node_getEntity( node ) ) );
101 Namespaced* namespaced = Node_getNamespaced( node );
102 if ( namespaced != 0 ) {
103 namespaced->setNamespace( GlobalNamespace() );
109 EntityCreator::KeyValueChangedFunc EntityKeyValues::m_entityKeyValueChanged = 0;
110 EntityCreator::KeyValueChangedFunc KeyValue::m_entityKeyValueChanged = 0;
111 Counter* EntityKeyValues::m_counter = 0;
113 bool g_showNames = true;
114 bool g_showAngles = true;
115 bool g_newLightDraw = true;
116 bool g_lightRadii = true;
118 class ConnectEntities
124 ConnectEntities( Entity* e1, Entity* e2, int index ) : m_e1( e1 ), m_e2( e2 ), m_index( index ){
126 const char *keyname(){
127 StringOutputStream key( 16 );
128 if ( m_index <= 0 ) {
131 if ( m_index == 1 ) {
134 key << "target" << m_index;
137 void connect( const char* name ){
138 m_e1->setKeyValue( keyname(), name );
139 m_e2->setKeyValue( "targetname", name );
141 typedef MemberCaller1<ConnectEntities, const char*, &ConnectEntities::connect> ConnectCaller;
144 inline Entity* ScenePath_getEntity( const scene::Path& path ){
145 Entity* entity = Node_getEntity( path.top() );
147 entity = Node_getEntity( path.parent() );
152 class Quake3EntityCreator : public EntityCreator
155 scene::Node& createEntity( EntityClass* eclass ){
156 return node_for_eclass( eclass );
158 void setKeyValueChangedFunc( KeyValueChangedFunc func ){
159 EntityKeyValues::setKeyValueChangedFunc( func );
161 void setCounter( Counter* counter ){
162 EntityKeyValues::setCounter( counter );
164 void connectEntities( const scene::Path& path, const scene::Path& targetPath, int index ){
165 Entity* e1 = ScenePath_getEntity( path );
166 Entity* e2 = ScenePath_getEntity( targetPath );
168 if ( e1 == 0 || e2 == 0 ) {
169 globalErrorStream() << "entityConnectSelected: both of the selected instances must be an entity\n";
174 globalErrorStream() << "entityConnectSelected: the selected instances must not both be from the same entity\n";
179 UndoableCommand undo( "entityConnectSelected" );
181 if ( g_gameType == eGameTypeDoom3 ) {
182 StringOutputStream key( 16 );
188 e1->setKeyValue( key.c_str(), e2->getKeyValue( "name" ) );
193 for ( unsigned int i = 0; ; ++i )
199 const char* value = e1->getKeyValue( key.c_str() );
200 if ( string_empty( value ) ) {
201 e1->setKeyValue( key.c_str(), e2->getKeyValue( "name" ) );
210 ConnectEntities connector( e1, e2, index );
213 const char* value = e2->getKeyValue( "targetname" );
214 if ( !string_empty( value ) ) {
215 connector.connect( value );
219 const char* type = e2->getKeyValue( "classname" );
220 if ( string_empty( type ) ) {
223 StringOutputStream key( 64 );
225 GlobalNamespace().makeUnique( key.c_str(), ConnectEntities::ConnectCaller( connector ) );
230 //prioritize existing target key
231 //checking, if ent got other connected ones already, could be better solution
232 const char* value = e1->getKeyValue( "target" );
233 if ( !string_empty( value ) ) {
234 connector.connect( value );
237 value = e2->getKeyValue( "targetname" );
238 if ( !string_empty( value ) ) {
239 connector.connect( value );
242 const char* type = e2->getKeyValue( "classname" );
243 if ( string_empty( type ) ) {
246 StringOutputStream key( 64 );
248 GlobalNamespace().makeUnique( key.c_str(), ConnectEntities::ConnectCaller( connector ) );
255 void setLightRadii( bool lightRadii ){
256 g_lightRadii = lightRadii;
258 bool getLightRadii(){
261 void setShowNames( bool showNames ){
262 g_showNames = showNames;
267 void setShowAngles( bool showAngles ){
268 g_showAngles = showAngles;
270 bool getShowAngles(){
274 void printStatistics() const {
275 StringPool_analyse( EntityKeyValues::getPool() );
279 Quake3EntityCreator g_Quake3EntityCreator;
281 EntityCreator& GetEntityCreator(){
282 return g_Quake3EntityCreator;
287 class filter_entity_classname : public EntityFilter
289 const char* m_classname;
291 filter_entity_classname( const char* classname ) : m_classname( classname ){
293 bool filter( const Entity& entity ) const {
294 return string_equal( entity.getKeyValue( "classname" ), m_classname );
298 class filter_entity_classgroup : public EntityFilter
300 const char* m_classgroup;
301 std::size_t m_length;
303 filter_entity_classgroup( const char* classgroup ) : m_classgroup( classgroup ), m_length( string_length( m_classgroup ) ){
305 bool filter( const Entity& entity ) const {
306 return string_equal_n( entity.getKeyValue( "classname" ), m_classgroup, m_length );
310 //filter_entity_classname g_filter_entity_world( "worldspawn" );
311 filter_entity_classname g_filter_entity_func_group( "func_group" );
312 filter_entity_classname g_filter_entity_light( "light" );
313 filter_entity_classname g_filter_entity_misc_model( "misc_model" );
314 filter_entity_classname g_filter_entity_misc_gamemodel( "misc_gamemodel" );
315 filter_entity_classgroup g_filter_entity_trigger( "trigger_" );
316 filter_entity_classgroup g_filter_entity_path( "path_" );
318 class filter_entity_doom3model : public EntityFilter
321 bool filter( const Entity& entity ) const {
322 return string_equal( entity.getKeyValue( "classname" ), "func_static" )
323 && !string_equal( entity.getKeyValue( "model" ), entity.getKeyValue( "name" ) );
327 filter_entity_doom3model g_filter_entity_doom3model;
330 class filter_entity_world : public EntityFilter
333 bool filter( const Entity& entity ) const {
334 return string_equal( entity.getKeyValue( "classname" ), "worldspawn" )
335 || string_equal( entity.getKeyValue( "classname" ), "func_group" );
339 filter_entity_world g_filter_entity_world;
341 void Entity_InitFilters(){
342 add_entity_filter( g_filter_entity_world, EXCLUDE_WORLD );
343 add_entity_filter( g_filter_entity_func_group, EXCLUDE_FUNC_GROUPS );
344 add_entity_filter( g_filter_entity_world, EXCLUDE_ENT, true );
345 add_entity_filter( g_filter_entity_trigger, EXCLUDE_TRIGGERS );
346 add_entity_filter( g_filter_entity_misc_model, EXCLUDE_MODELS );
347 add_entity_filter( g_filter_entity_misc_gamemodel, EXCLUDE_MODELS );
348 add_entity_filter( g_filter_entity_doom3model, EXCLUDE_MODELS );
349 add_entity_filter( g_filter_entity_light, EXCLUDE_LIGHTS );
350 add_entity_filter( g_filter_entity_path, EXCLUDE_PATHS );
354 #include "preferencesystem.h"
356 void Entity_Construct( EGameType gameType ){
357 g_gameType = gameType;
358 if ( g_gameType == eGameTypeDoom3 ) {
359 g_targetable_nameKey = "name";
361 Static<KeyIsName>::instance().m_keyIsName = keyIsNameDoom3;
362 Static<KeyIsName>::instance().m_nameKey = "name";
366 Static<KeyIsName>::instance().m_keyIsName = keyIsNameQuake3;
367 Static<KeyIsName>::instance().m_nameKey = "targetname";
370 GlobalPreferenceSystem().registerPreference( "SI_ShowNames", BoolImportStringCaller( g_showNames ), BoolExportStringCaller( g_showNames ) );
371 GlobalPreferenceSystem().registerPreference( "SI_ShowAngles", BoolImportStringCaller( g_showAngles ), BoolExportStringCaller( g_showAngles ) );
372 GlobalPreferenceSystem().registerPreference( "NewLightStyle", BoolImportStringCaller( g_newLightDraw ), BoolExportStringCaller( g_newLightDraw ) );
373 GlobalPreferenceSystem().registerPreference( "LightRadiuses", BoolImportStringCaller( g_lightRadii ), BoolExportStringCaller( g_lightRadii ) );
375 Entity_InitFilters();
376 LightType lightType = LIGHTTYPE_DEFAULT;
377 if ( g_gameType == eGameTypeRTCW ) {
378 lightType = LIGHTTYPE_RTCW;
380 else if ( g_gameType == eGameTypeDoom3 ) {
381 lightType = LIGHTTYPE_DOOM3;
383 Light_Construct( lightType );
384 MiscModel_construct();
385 Doom3Group_construct();
387 RenderablePivot::StaticShader::instance() = GlobalShaderCache().capture( "$PIVOT" );
389 GlobalShaderCache().attachRenderable( StaticRenderableConnectionLines::instance() );
392 void Entity_Destroy(){
393 GlobalShaderCache().detachRenderable( StaticRenderableConnectionLines::instance() );
395 GlobalShaderCache().release( "$PIVOT" );
397 Doom3Group_destroy();